From 04cb9bad5b58ee0525bc35afc32c39bea3c7c2e6 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Thu, 9 May 2024 16:40:28 +0800 Subject: [PATCH 1/5] =?UTF-8?q?=E3=80=90=E4=BF=AE=E5=A4=8D=E3=80=91?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20imagine=20=E7=89=88=E6=9C=AC=E8=BF=87?= =?UTF-8?q?=E6=9C=9F=EF=BC=8C=E4=B8=8D=E8=83=BD=E6=8F=90=E4=BA=A4=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E9=97=AE=E9=A2=98=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/http-body/imagine.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json index f4554cc50..4182d8bd1 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/http-body/imagine.json @@ -6,7 +6,7 @@ "session_id": "$session_id", "nonce": "$nonce", "data": { - "version": "1166847114203123795", + "version": "1237876415471554623", "id": "938956540159881230", "name": "imagine", "type": 1, From caca47b6d786008a0b94266b17c75eed8e7d1508 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 9 May 2024 23:03:10 +0800 Subject: [PATCH 2/5] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9AAPI=20=E5=AF=86=E9=92=A5=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/ErrorCodeConstants.java | 8 ++- .../iocoder/yudao/module/ai/package-info.java | 5 -- .../admin/model/AiApiKeyController.java | 72 +++++++++++++++++++ .../model/vo/apikey/AiApiKeyPageReqVO.java | 27 +++++++ .../admin/model/vo/apikey/AiApiKeyRespVO.java | 28 ++++++++ .../model/vo/apikey/AiApiKeySaveReqVO.java | 34 +++++++++ .../model/vo/model/AiChatModelListRespVO.java | 4 -- .../ai/dal/dataobject/model/AiApiKeyDO.java | 12 ++-- .../ai/dal/mysql/model/AiApiKeyMapper.java | 26 +++++++ .../ai/service/model/AiApiKeyService.java | 54 ++++++++++++++ .../ai/service/model/AiApiKeyServiceImpl.java | 70 ++++++++++++++++++ .../yudao/framework/ai/AiPlatformEnum.java | 4 +- 12 files changed, 322 insertions(+), 22 deletions(-) delete mode 100644 yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java index fde06746e..302c13a84 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java @@ -3,13 +3,14 @@ package cn.iocoder.yudao.module.ai; import cn.iocoder.yudao.framework.common.exception.ErrorCode; /** - * System 错误码枚举类 + * AI 错误码枚举类 * - * system 系统,使用 1-002-000-000 段 + * ai 系统,使用 1-040-000-000 段 */ public interface ErrorCodeConstants { - // ========== 模块 ai 错误码区间 [1-022-000-000 ~ 1-023-000-000) ========== + // ========== API 密钥 1-040-000-000 ========== + ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "AI API 密钥不存在"); // chat @@ -37,4 +38,5 @@ public interface ErrorCodeConstants { ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); ErrorCode AI_MODAL_DISABLE_NOT_USED = new ErrorCode(1_022_000_084, "AI 模型禁用不能使用!"); + } diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java deleted file mode 100644 index 6fa1f167a..000000000 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/package-info.java +++ /dev/null @@ -1,5 +0,0 @@ -/** - * author: fansili - * time: 2024/3/3 18:14 - */ -package cn.iocoder.yudao.module.ai; \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java new file mode 100644 index 000000000..7add01901 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java @@ -0,0 +1,72 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import cn.iocoder.yudao.module.ai.service.model.AiApiKeyService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "管理后台 - AI API 密钥") +@RestController +@RequestMapping("/ai/api-key") +@Validated +public class AiApiKeyController { + + @Resource + private AiApiKeyService apiKeyService; + + @PostMapping("/create") + @Operation(summary = "创建AI API 密钥") + @PreAuthorize("@ss.hasPermission('ai:api-key:create')") + public CommonResult createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) { + return success(apiKeyService.createApiKey(createReqVO)); + } + + @PutMapping("/update") + @Operation(summary = "更新AI API 密钥") + @PreAuthorize("@ss.hasPermission('ai:api-key:update')") + public CommonResult updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) { + apiKeyService.updateApiKey(updateReqVO); + return success(true); + } + + @DeleteMapping("/delete") + @Operation(summary = "删除AI API 密钥") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:api-key:delete')") + public CommonResult deleteApiKey(@RequestParam("id") Long id) { + apiKeyService.deleteApiKey(id); + return success(true); + } + + @GetMapping("/get") + @Operation(summary = "获得AI API 密钥") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:api-key:query')") + public CommonResult getApiKey(@RequestParam("id") Long id) { + AiApiKeyDO apiKey = apiKeyService.getApiKey(id); + return success(BeanUtils.toBean(apiKey, AiApiKeyRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得AI API 密钥分页") + @PreAuthorize("@ss.hasPermission('ai:api-key:query')") + public CommonResult> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) { + PageResult pageResult = apiKeyService.getApiKeyPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiApiKeyRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java new file mode 100644 index 000000000..021e0f80c --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyPageReqVO.java @@ -0,0 +1,27 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey; + +import lombok.*; +import java.util.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; +import org.springframework.format.annotation.DateTimeFormat; +import java.time.LocalDateTime; + +import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; + +@Schema(description = "管理后台 - AI API 密钥分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiApiKeyPageReqVO extends PageParam { + + @Schema(description = "名称", example = "文心一言") + private String name; + + @Schema(description = "平台", example = "OpenAI") + private String platform; + + @Schema(description = "状态", example = "1") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java new file mode 100644 index 000000000..55d6d802b --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeyRespVO.java @@ -0,0 +1,28 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +@Schema(description = "管理后台 - AI API 密钥 Response VO") +@Data +public class AiApiKeyRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538") + private Long id; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言") + private String name; + + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + private String apiKey; + + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") + private String platform; + + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com") + private String url; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java new file mode 100644 index 000000000..8fbc8fde7 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/apikey/AiApiKeySaveReqVO.java @@ -0,0 +1,34 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import java.util.*; +import jakarta.validation.constraints.*; + +@Schema(description = "管理后台 - AI API 密钥新增/修改 Request VO") +@Data +public class AiApiKeySaveReqVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23538") + private Long id; + + @Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "文心一言") + @NotEmpty(message = "名称不能为空") + private String name; + + @Schema(description = "密钥", requiredMode = Schema.RequiredMode.REQUIRED, example = "ABC") + @NotEmpty(message = "密钥不能为空") + private String apiKey; + + @Schema(description = "平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") + @NotEmpty(message = "平台不能为空") + private String platform; + + @Schema(description = "自定义 API 地址", example = "https://aip.baidubce.com") + private String url; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "状态不能为空") + private Integer status; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java index 59a8900a6..916106a23 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java @@ -1,9 +1,5 @@ package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; -import cn.iocoder.yudao.framework.ai.AiPlatformEnum; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; -import com.baomidou.mybatisplus.annotation.TableId; import io.swagger.v3.oas.annotations.media.Schema; import lombok.Data; import lombok.experimental.Accessors; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java index 9512f4c5e..306c1e086 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/model/AiApiKeyDO.java @@ -31,18 +31,16 @@ public class AiApiKeyDO extends BaseDO { * 名称 */ private String name; + /** + * 密钥 + */ + private String apiKey; /** * 平台 * * 枚举 {@link AiPlatformEnum} */ private String platform; - /** - * 用途 - * - * TODO 芋艿:枚举;chat、image - */ - private Integer type; /** * API 地址 */ @@ -54,6 +52,4 @@ public class AiApiKeyDO extends BaseDO { */ private Integer status; - // TODO 芋艿:proxyUrl 代理地址 - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java new file mode 100644 index 000000000..fef4965b8 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/model/AiApiKeyMapper.java @@ -0,0 +1,26 @@ +package cn.iocoder.yudao.module.ai.dal.mysql.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import org.apache.ibatis.annotations.Mapper; + +/** + * AI API 密钥 Mapper + * + * @author 芋道源码 + */ +@Mapper +public interface AiApiKeyMapper extends BaseMapperX { + + default PageResult selectPage(AiApiKeyPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiApiKeyDO::getName, reqVO.getName()) + .eqIfPresent(AiApiKeyDO::getPlatform, reqVO.getPlatform()) + .eqIfPresent(AiApiKeyDO::getStatus, reqVO.getStatus()) + .orderByDesc(AiApiKeyDO::getId)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java new file mode 100644 index 000000000..7390bc8e6 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java @@ -0,0 +1,54 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import jakarta.validation.Valid; + +/** + * AI API 密钥 Service 接口 + * + * @author 芋道源码 + */ +public interface AiApiKeyService { + + /** + * 创建AI API 密钥 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO); + + /** + * 更新AI API 密钥 + * + * @param updateReqVO 更新信息 + */ + void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO); + + /** + * 删除AI API 密钥 + * + * @param id 编号 + */ + void deleteApiKey(Long id); + + /** + * 获得AI API 密钥 + * + * @param id 编号 + * @return AI API 密钥 + */ + AiApiKeyDO getApiKey(Long id); + + /** + * 获得AI API 密钥分页 + * + * @param pageReqVO 分页查询 + * @return AI API 密钥分页 + */ + PageResult getApiKeyPage(AiApiKeyPageReqVO pageReqVO); + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java new file mode 100644 index 000000000..9bafb17d5 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -0,0 +1,70 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeySaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiApiKeyDO; +import cn.iocoder.yudao.module.ai.dal.mysql.model.AiApiKeyMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.API_KEY_NOT_EXISTS; + +/** + * AI API 密钥 Service 实现类 + * + * @author 芋道源码 + */ +@Service +@Validated +public class AiApiKeyServiceImpl implements AiApiKeyService { + + @Resource + private AiApiKeyMapper apiKeyMapper; + + @Override + public Long createApiKey(AiApiKeySaveReqVO createReqVO) { + // 插入 + AiApiKeyDO apiKey = BeanUtils.toBean(createReqVO, AiApiKeyDO.class); + apiKeyMapper.insert(apiKey); + // 返回 + return apiKey.getId(); + } + + @Override + public void updateApiKey(AiApiKeySaveReqVO updateReqVO) { + // 校验存在 + validateApiKeyExists(updateReqVO.getId()); + // 更新 + AiApiKeyDO updateObj = BeanUtils.toBean(updateReqVO, AiApiKeyDO.class); + apiKeyMapper.updateById(updateObj); + } + + @Override + public void deleteApiKey(Long id) { + // 校验存在 + validateApiKeyExists(id); + // 删除 + apiKeyMapper.deleteById(id); + } + + private void validateApiKeyExists(Long id) { + if (apiKeyMapper.selectById(id) == null) { + throw exception(API_KEY_NOT_EXISTS); + } + } + + @Override + public AiApiKeyDO getApiKey(Long id) { + return apiKeyMapper.selectById(id); + } + + @Override + public PageResult getApiKeyPage(AiApiKeyPageReqVO pageReqVO) { + return apiKeyMapper.selectPage(pageReqVO); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java index 752cb042d..427202391 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java @@ -6,6 +6,7 @@ import lombok.Getter; import java.util.List; +// TODO 芋艿:这块,看看要不要调整下; /** * ai 模型平台 * @@ -16,11 +17,10 @@ import java.util.List; @AllArgsConstructor public enum AiPlatformEnum { - YI_YAN("yiyan", "一言"), QIAN_WEN("qianwen", "千问"), XING_HUO("xinghuo", "星火"), - OPEN_AI("openai", "openAi"), + OPEN_AI("openai", "openAi"), // TODO 芋艿:OpenAI OPEN_AI_DALL("dall", "dall"), MIDJOURNEY("midjourney", "midjourney"), From 8e7fc1ff964325ac5583e268f2c7792a54eed5e5 Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 10 May 2024 15:29:11 +0800 Subject: [PATCH 3/5] =?UTF-8?q?=E3=80=90=E4=BC=98=E5=8C=96=E3=80=91?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E5=A2=9E=E5=8A=A0=20mjNonce=20?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=EF=BC=8C=E7=94=9F=E6=88=90=E5=A5=BD=E3=80=81?= =?UTF-8?q?=E6=88=96=E5=BC=82=E5=B8=B8=E7=9A=84=E5=9B=BE=E7=89=87=E4=BF=A1?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ai/dal/dataobject/image/AiImageDO.java | 4 +- .../module/ai/dal/mysql/AiImageMapper.java | 10 ++++ .../ai/service/impl/AiImageServiceImpl.java | 4 +- .../YuDaoMidjourneyMessageHandler.java | 12 ++--- .../src/main/resources/http/image.http | 2 +- .../imageGenerationProcess/test2.json | 54 +++++++++++++++++++ .../src/main/resources/application-local.yaml | 12 +++-- 7 files changed, 81 insertions(+), 17 deletions(-) create mode 100644 yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java index ea83d2181..c04fea12f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java @@ -49,8 +49,8 @@ public class AiImageDO extends BaseDO { // ============ mj 需要字段 - @Schema(description = "用户操作的消息编号(MJ返回)") - private String mjMessageId; + @Schema(description = "用户操作的Nonce编号(MJ返回)") + private String mjNonceId; @Schema(description = "用户操作的操作编号(MJ返回)") private String mjOperationId; diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java index ef2b6a596..1ee5b436d 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiImageMapper.java @@ -1,6 +1,7 @@ package cn.iocoder.yudao.module.ai.dal.mysql; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; +import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; import org.apache.ibatis.annotations.Mapper; import org.springframework.stereotype.Repository; @@ -17,4 +18,13 @@ import org.springframework.stereotype.Repository; public interface AiImageMapper extends BaseMapperX { + /** + * 更新 - 根据 messageId + * + * @param mjNonceId + * @param aiImageDO + */ + default void updateByMjNonce(Long mjNonceId, AiImageDO aiImageDO) { + this.update(aiImageDO, new LambdaQueryWrapperX().eq(AiImageDO::getMjNonceId, mjNonceId)); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java index 19281d4df..d306b4b2f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiImageServiceImpl.java @@ -145,7 +145,7 @@ public class AiImageServiceImpl implements AiImageService { // 校验 OperateId 是否存在 AiImageMidjourneyOperationsVO midjourneyOperationsVO = validateMidjourneyOperationsExists(midjourneyOperations, req.getOperateId()); // 校验 messageId - validateMessageId(aiImageDO.getMjMessageId(), req.getMessageId()); + validateMessageId(aiImageDO.getMjNonceId(), req.getMessageId()); // 获取 mjOperationName String mjOperationName = midjourneyOperationsVO.getLabel(); // 保存一个 image 任务记录 @@ -222,7 +222,7 @@ public class AiImageServiceImpl implements AiImageService { aiImageDO.setDrawingImageUrl(drawingImageUrl); aiImageDO.setDrawingErrorMessage(drawingErrorMessage); // - aiImageDO.setMjMessageId(mjMessageId); + aiImageDO.setMjNonceId(mjMessageId); aiImageDO.setMjOperationId(mjOperationId); aiImageDO.setMjOperationName(mjOperationName); aiImageMapper.insert(aiImageDO); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java index 43ca13b6a..24f70b782 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java @@ -62,12 +62,11 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { private void errorHandler(MidjourneyMessage midjourneyMessage) { // image 编号 - Long aiImageId = Long.valueOf(midjourneyMessage.getNonce()); + Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); // 获取 error message String errorMessage = getErrorMessage(midjourneyMessage); - aiImageMapper.updateById( + aiImageMapper.updateByMjNonce(nonceId, new AiImageDO() - .setId(aiImageId) .setDrawingErrorMessage(errorMessage) .setDrawingStatus(AiImageDrawingStatusEnum.FAIL.getStatus()) ); @@ -83,7 +82,7 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { private void successHandler(MidjourneyMessage midjourneyMessage) { // 获取id - Long aiImageId = Long.valueOf(midjourneyMessage.getNonce()); + Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); // 获取生成 url String imageUrl = null; if (CollUtil.isNotEmpty(midjourneyMessage.getAttachments())) { @@ -102,12 +101,11 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { // 获取 midjourneyOperations List midjourneyOperations = getMidjourneyOperationsList(midjourneyMessage); // 更新数据库 - aiImageMapper.updateById( + aiImageMapper.updateByMjNonce(nonceId, new AiImageDO() - .setId(aiImageId) .setDrawingImageUrl(imageUrl) .setDrawingStatus(drawingStatusEnum == null ? null : drawingStatusEnum.getStatus()) - .setMjMessageId(midjourneyMessage.getId()) + .setMjNonceId(midjourneyMessage.getId()) .setMjOperations(JsonUtils.toJsonString(midjourneyOperations)) ); } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http b/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http index 3f360aee1..bf1165773 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/resources/http/image.http @@ -20,5 +20,5 @@ Content-Type: application/json Authorization: {{token}} { - "prompt": "Cute cartoon style mobile game scene, a colorful camping car with an outdoor table and chairs next to it on the road in a spring forest, the simple structure of the camper van, soft lighting, C4D rendering, 3d model in the style of a cartoon, cute shape, a pastel color scheme, closeup view from the side angle, high resolution, bright colors, a happy atmosphere. --ar 1:2 --v 6.0" + "prompt": "Cute cartoon style mobile game scene, a colorful camping car with an outdoor table and chairs next to it on the road in a spring forest, the simple structure of the camper van, soft lighting, C4D rendering, 3d model in the style of a cartoon, cute shape, a pastel color scheme, closeup view from the side angle, high resolution, bright colors, a happy atmosphere." } \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json new file mode 100644 index 000000000..172152804 --- /dev/null +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/resources/requestTestJson/imageGenerationProcess/test2.json @@ -0,0 +1,54 @@ +{ + "type": 2, + "application_id": "936929561302675456", + "guild_id": "1237948819677904956", + "channel_id": "1237948819677904960", + "session_id": "4bdb0c32158f625bbd7f0a54bfbb54aa", + "data": { + "version": "1237876415471554623", + "id": "938956540159881230", + "name": "imagine", + "type": 1, + "options": [ + { + "type": 3, + "name": "prompt", + "value": "哈哈哈" + } + ], + "application_command": { + "id": "938956540159881230", + "type": 1, + "application_id": "936929561302675456", + "version": "1237876415471554623", + "name": "imagine", + "description": "Create images with Midjourney", + "options": [ + { + "type": 3, + "name": "prompt", + "description": "The prompt to imagine", + "required": true, + "description_localized": "The prompt to imagine", + "name_localized": "prompt" + } + ], + "dm_permission": true, + "contexts": [ + 0, + 1, + 2 + ], + "integration_types": [ + 0, + 1 + ], + "global_popularity_rank": 1, + "description_localized": "Create images with Midjourney", + "name_localized": "imagine" + }, + "attachments": [] + }, + "nonce": "1238062212925358080", + "analytics_location": "slash_ui" +} \ No newline at end of file diff --git a/yudao-server/src/main/resources/application-local.yaml b/yudao-server/src/main/resources/application-local.yaml index 1c16251d7..959988d7d 100644 --- a/yudao-server/src/main/resources/application-local.yaml +++ b/yudao-server/src/main/resources/application-local.yaml @@ -52,7 +52,7 @@ spring: # url: jdbc:sqlserver://127.0.0.1:1433;DatabaseName=ruoyi-vue-pro;SelectMethod=cursor;encrypt=false;rewriteBatchedStatements=true;useUnicode=true;characterEncoding=utf-8 # SQLServer 连接的示例 # url: jdbc:dm://127.0.0.1:5236?schema=RUOYI_VUE_PRO # DM 连接的示例 username: root - password: 123456 + password: root # username: sa # SQL Server 连接的示例 # password: Yudao@2024 # SQL Server 连接的示例 # username: SYSDBA # DM 连接的示例 @@ -61,7 +61,7 @@ spring: lazy: true # 开启懒加载,保证启动速度 url: jdbc:mysql://127.0.0.1:3306/ruoyi-vue-pro?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&nullCatalogMeansCurrent=true username: root - password: 123456 + password: root # Redis 配置。Redisson 默认的配置足够使用,一般不需要进行调优 data: @@ -248,9 +248,9 @@ yudao: style: vivid midjourney: enable: true - token: OTc1MzcyNDg1OTcxMzEyNzAw.G2iiSo.OqW9vToC5dokiyb1QOWnCwRPsYpOjLyNcf9--M - guild-id: 1234355413420347402 - channel-id: 1234380679576424448 + token: MTE4MjE3MjY2MjkxNTY3ODIzOA.GEV1SG.c49F8lZoGCUHwsj8O0UdodmM6nyQHvuD2fXflw + guild-id: 1237948819677904956 + channel-id: 1237948819677904960 captcha: enable: false # 本地环境,暂时关闭图片验证码,方便登录等接口的测试; security: @@ -267,6 +267,8 @@ yudao: enable: false demo: false # 关闭演示模式 tencent-lbs-key: TVDBZ-TDILD-4ON4B-PFDZA-RNLKH-VVF6E # QQ 地图的密钥 https://lbs.qq.com/service/staticV2/staticGuide/staticDoc + tenant: + enable: false justauth: enabled: true From c1e2ba9ed4dd069835844d2c71b69795809e985a Mon Sep 17 00:00:00 2001 From: cherishsince Date: Fri, 10 May 2024 17:26:10 +0800 Subject: [PATCH 4/5] =?UTF-8?q?=E3=80=90todo=E3=80=91mj=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=20=E5=A2=9E=E5=8A=A0=20todo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java | 1 + 1 file changed, 1 insertion(+) diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java index 24f70b782..0260a1511 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/midjourneyHandler/YuDaoMidjourneyMessageHandler.java @@ -83,6 +83,7 @@ public class YuDaoMidjourneyMessageHandler implements MidjourneyMessageHandler { private void successHandler(MidjourneyMessage midjourneyMessage) { // 获取id Long nonceId = Long.valueOf(midjourneyMessage.getNonce()); + // TODO @芋艿 这个地方有问题,不能根据 nonce来更新,不返回这个信息(别人获取了 image-xxx-xx 后面一段hash,由于没有mj账号测试,暂不清楚。) // 获取生成 url String imageUrl = null; if (CollUtil.isNotEmpty(midjourneyMessage.getAttachments())) { From b9deba889c64e5e24fa068c8ab70e44beaaa8e7b Mon Sep 17 00:00:00 2001 From: YunaiV Date: Fri, 10 May 2024 22:48:49 +0800 Subject: [PATCH 5/5] =?UTF-8?q?=E3=80=90=E6=96=B0=E5=A2=9E=E3=80=91AI?= =?UTF-8?q?=EF=BC=9AAPI=20=E6=A8=A1=E5=9E=8B=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../yudao/module/ai/ErrorCodeConstants.java | 16 +-- .../admin/model/AiApiKeyController.java | 10 +- .../admin/model/AiChatModelController.java | 93 +++++++------ .../admin/model/AiChatRoleController.java | 23 ++-- .../vo/chatModel/AiChatModelPageReqVO.java | 22 +++ .../model/vo/chatModel/AiChatModelRespVO.java | 45 +++++++ .../vo/chatModel/AiChatModelSaveReqVO.java | 50 +++++++ .../model/vo/model/AiChatModalRespVO.java | 50 ------- .../vo/model/AiChatModalUpdateReqVO.java | 63 --------- .../model/vo/model/AiChatModelAddReqVO.java | 53 -------- .../model/vo/model/AiChatModelListReqVO.java | 22 --- .../model/vo/model/AiChatModelListRespVO.java | 50 ------- .../vo/model/AiChatModelUpdateReqVO.java | 57 -------- .../ai/convert/AiChatMessageConvert.java | 8 -- .../module/ai/convert/AiChatModelConvert.java | 57 -------- .../ai/dal/mysql/AiChatModelMapper.java | 15 ++- .../module/ai/service/AiChatModelService.java | 67 --------- .../module/ai/service/AiChatRoleService.java | 1 + .../service/impl/AiChatModalServiceImpl.java | 127 ------------------ .../service/impl/AiChatRoleServiceImpl.java | 10 +- .../ai/service/impl/AiChatServiceImpl.java | 26 ++-- .../ai/service/model/AiApiKeyService.java | 22 ++- .../ai/service/model/AiApiKeyServiceImpl.java | 18 ++- .../AiChatConversationServiceImpl.java | 8 +- .../ai/service/model/AiChatModelService.java | 63 +++++++++ .../service/model/AiChatModelServiceImpl.java | 92 +++++++++++++ .../yudao/framework/ai/AiPlatformEnum.java | 2 +- 27 files changed, 407 insertions(+), 663 deletions(-) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java delete mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java rename yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/{impl => model}/AiChatConversationServiceImpl.java (93%) create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java create mode 100644 yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java diff --git a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java index 302c13a84..a3c343e12 100644 --- a/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java +++ b/yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/ErrorCodeConstants.java @@ -11,10 +11,15 @@ public interface ErrorCodeConstants { // ========== API 密钥 1-040-000-000 ========== ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "AI API 密钥不存在"); + ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "AI API 密钥已禁用!"); - // chat + // ========== API 聊天模型 1-040-001-000 ========== - ErrorCode AI_MODULE_NOT_SUPPORTED = new ErrorCode(1_022_000_000, "AI 模型暂不支持!"); + ErrorCode CHAT_MODAL_NOT_EXIST = new ErrorCode(1_040_001_000, "AI 模型不存在!"); + ErrorCode CHAT_MODAL_DISABLE = new ErrorCode(1_040_001_001, "AI 模型({})已禁用!"); + + // ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} "); +// ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); // conversation @@ -31,12 +36,5 @@ public interface ErrorCodeConstants { ErrorCode AI_CHAT_ROLE_NOT_EXIST = new ErrorCode(1_022_000_060, "AI 角色不存在!"); ErrorCode AI_CHAT_ROLE_NOT_PUBLIC = new ErrorCode(1_022_000_060, "AI 角色未公开!"); - // modal - - ErrorCode AI_MODAL_NOT_EXIST = new ErrorCode(1_022_000_080, "AI 模型不存在!"); - ErrorCode AI_MODAL_CONFIG_PARAMS_INCORRECT = new ErrorCode(1_022_000_081, "AI 模型 config 参数不正确! {} "); - ErrorCode AI_MODAL_PLATFORM_PARAMS_INCORRECT = new ErrorCode(1_022_000_083, "AI 平台参数不正确! {} "); - ErrorCode AI_MODAL_DISABLE_NOT_USED = new ErrorCode(1_022_000_084, "AI 模型禁用不能使用!"); - } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java index 7add01901..78b588647 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiApiKeyController.java @@ -29,14 +29,14 @@ public class AiApiKeyController { private AiApiKeyService apiKeyService; @PostMapping("/create") - @Operation(summary = "创建AI API 密钥") + @Operation(summary = "创建 API 密钥") @PreAuthorize("@ss.hasPermission('ai:api-key:create')") public CommonResult createApiKey(@Valid @RequestBody AiApiKeySaveReqVO createReqVO) { return success(apiKeyService.createApiKey(createReqVO)); } @PutMapping("/update") - @Operation(summary = "更新AI API 密钥") + @Operation(summary = "更新 API 密钥") @PreAuthorize("@ss.hasPermission('ai:api-key:update')") public CommonResult updateApiKey(@Valid @RequestBody AiApiKeySaveReqVO updateReqVO) { apiKeyService.updateApiKey(updateReqVO); @@ -44,7 +44,7 @@ public class AiApiKeyController { } @DeleteMapping("/delete") - @Operation(summary = "删除AI API 密钥") + @Operation(summary = "删除 API 密钥") @Parameter(name = "id", description = "编号", required = true) @PreAuthorize("@ss.hasPermission('ai:api-key:delete')") public CommonResult deleteApiKey(@RequestParam("id") Long id) { @@ -53,7 +53,7 @@ public class AiApiKeyController { } @GetMapping("/get") - @Operation(summary = "获得AI API 密钥") + @Operation(summary = "获得 API 密钥") @Parameter(name = "id", description = "编号", required = true, example = "1024") @PreAuthorize("@ss.hasPermission('ai:api-key:query')") public CommonResult getApiKey(@RequestParam("id") Long id) { @@ -62,7 +62,7 @@ public class AiApiKeyController { } @GetMapping("/page") - @Operation(summary = "获得AI API 密钥分页") + @Operation(summary = "获得 API 密钥分页") @PreAuthorize("@ss.hasPermission('ai:api-key:query')") public CommonResult> getApiKeyPage(@Valid AiApiKeyPageReqVO pageReqVO) { PageResult pageResult = apiKeyService.getApiKeyPage(pageReqVO); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java index 010d143cc..7d73e222a 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatModelController.java @@ -2,60 +2,71 @@ package cn.iocoder.yudao.module.ai.controller.admin.model; import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelAddReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelUpdateReqVO; -import cn.iocoder.yudao.module.ai.service.AiChatModelService; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelRespVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; +import jakarta.annotation.Resource; +import jakarta.validation.Valid; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -// TODO @fan:调整下接口;相关 vo 的命名等等;modal => model +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; -/** - * ai 模型 - * - * @author fansili - * @time 2024/4/24 19:42 - * @since 1.0 - */ -@Tag(name = "A6-AI模型") +@Tag(name = "管理后台 - AI 聊天模型") @RestController -@RequestMapping("/ai/chat/model") -@Slf4j -@AllArgsConstructor +@RequestMapping("/ai/chat-model") +@Validated public class AiChatModelController { - private final AiChatModelService aiChatModelService; + @Resource + private AiChatModelService chatModelService; - @Operation(summary = "ai模型 - 模型列表") - @GetMapping("/list") - public PageResult list(@ModelAttribute AiChatModelListReqVO req) { - return aiChatModelService.list(req); + @PostMapping("/create") + @Operation(summary = "创建聊天模型") + @PreAuthorize("@ss.hasPermission('ai:chat-model:create')") + public CommonResult createChatModel(@Valid @RequestBody AiChatModelSaveReqVO createReqVO) { + return success(chatModelService.createChatModel(createReqVO)); } - @Operation(summary = "ai模型 - 添加") - @PutMapping("/add") - public CommonResult add(@RequestBody @Validated AiChatModelAddReqVO req) { - aiChatModelService.add(req); - return CommonResult.success(null); + @PutMapping("/update") + @Operation(summary = "更新聊天模型") + @PreAuthorize("@ss.hasPermission('ai:chat-model:update')") + public CommonResult updateChatModel(@Valid @RequestBody AiChatModelSaveReqVO updateReqVO) { + chatModelService.updateChatModel(updateReqVO); + return success(true); } - @Operation(summary = "ai模型 - 修改") - @PostMapping("/update") - public CommonResult update(@RequestBody @Validated AiChatModelUpdateReqVO req) { - aiChatModelService.update(req); - return CommonResult.success(null); - } - - @Operation(summary = "ai模型 - 删除") @DeleteMapping("/delete") - public CommonResult delete(@RequestParam("id") Long id) { - aiChatModelService.delete(id); - return CommonResult.success(null); + @Operation(summary = "删除聊天模型") + @Parameter(name = "id", description = "编号", required = true) + @PreAuthorize("@ss.hasPermission('ai:chat-model:delete')") + public CommonResult deleteChatModel(@RequestParam("id") Long id) { + chatModelService.deleteChatModel(id); + return success(true); } -} + + @GetMapping("/get") + @Operation(summary = "获得聊天模型") + @Parameter(name = "id", description = "编号", required = true, example = "1024") + @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") + public CommonResult getChatModel(@RequestParam("id") Long id) { + AiChatModelDO chatModel = chatModelService.getChatModel(id); + return success(BeanUtils.toBean(chatModel, AiChatModelRespVO.class)); + } + + @GetMapping("/page") + @Operation(summary = "获得聊天模型分页") + @PreAuthorize("@ss.hasPermission('ai:chat-model:query')") + public CommonResult> getChatModelPage(@Valid AiChatModelPageReqVO pageReqVO) { + PageResult pageResult = chatModelService.getChatModelPage(pageReqVO); + return success(BeanUtils.toBean(pageResult, AiChatModelRespVO.class)); + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java index 1aab42600..1de4b3991 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/AiChatRoleController.java @@ -6,26 +6,20 @@ import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.*; import cn.iocoder.yudao.module.ai.service.AiChatRoleService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.annotation.Resource; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; -// TODO @fan:调整下接口;相关 vo 的命名等等;modal => model -/** - * ai chat 角色 - * - * @fansili - * @since v1.0 - */ -@Tag(name = "A4-chat角色") +@Tag(name = "管理后台 - AI 聊天角色") @RestController -@RequestMapping("/ai/chat/role") -@Slf4j -@AllArgsConstructor +@RequestMapping("/ai/chat-role") +@Validated public class AiChatRoleController { - private final AiChatRoleService chatRoleService; + @Resource + private AiChatRoleService chatRoleService; @Operation(summary = "chat角色 - 角色列表") @GetMapping("/list") @@ -60,4 +54,9 @@ public class AiChatRoleController { chatRoleService.delete(id); return CommonResult.success(null); } + + // ========== 角色管理 ========== + + + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java new file mode 100644 index 000000000..0971522eb --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelPageReqVO.java @@ -0,0 +1,22 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; + +import lombok.*; +import io.swagger.v3.oas.annotations.media.Schema; +import cn.iocoder.yudao.framework.common.pojo.PageParam; + +@Schema(description = "管理后台 - API 聊天模型分页 Request VO") +@Data +@EqualsAndHashCode(callSuper = true) +@ToString(callSuper = true) +public class AiChatModelPageReqVO extends PageParam { + + @Schema(description = "模型名字", example = "张三") + private String name; + + @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125") + private String model; + + @Schema(description = "模型平台", example = "OpenAI") + private String platform; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java new file mode 100644 index 000000000..681dabe68 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelRespVO.java @@ -0,0 +1,45 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Schema(description = "管理后台 - AI 聊天模型 Response VO") +@Data +public class AiChatModelRespVO { + + @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2630") + private Long id; + + @Schema(description = "API 秘钥编号", example = "22042") + private Long keyId; + + @Schema(description = "模型名字", example = "张三") + private String name; + + @Schema(description = "模型标识", example = "gpt-3.5-turbo-0125") + private String model; + + @Schema(description = "模型平台", example = "OpenAI") + private String platform; + + @Schema(description = "排序", example = "1") + private Integer sort; + + @Schema(description = "状态", example = "2") + private Integer status; + + @Schema(description = "温度参数", example = "1") + private Double temperature; + + @Schema(description = "单条回复的最大 Token 数量", example = "4096") + private Integer maxTokens; + + @Schema(description = "上下文的最大 Message 数量", example = "8192") + private Integer maxContexts; + + @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) + private LocalDateTime createTime; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java new file mode 100644 index 000000000..4fad5a1fc --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/chatModel/AiChatModelSaveReqVO.java @@ -0,0 +1,50 @@ +package cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel; + +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.validation.InEnum; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; +import jakarta.validation.constraints.*; + +@Schema(description = "管理后台 - API 聊天模型新增/修改 Request VO") +@Data +public class AiChatModelSaveReqVO { + + @Schema(description = "编号", example = "2630") + private Long id; + + @Schema(description = "API 秘钥编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22042") + @NotNull(message = "API 秘钥编号不能为空") + private Long keyId; + + @Schema(description = "模型名字", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三") + @NotEmpty(message = "模型名字不能为空") + private String name; + + @Schema(description = "模型标识", requiredMode = Schema.RequiredMode.REQUIRED, example = "gpt-3.5-turbo-0125") + @NotEmpty(message = "模型标识不能为空") + private String model; + + @Schema(description = "模型平台", requiredMode = Schema.RequiredMode.REQUIRED, example = "OpenAI") + @NotEmpty(message = "模型平台不能为空") + private String platform; + + @Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @NotNull(message = "排序不能为空") + private Integer sort; + + @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") + @InEnum(CommonStatusEnum.class) + @NotNull(message = "状态不能为空") + private Integer status; + + @Schema(description = "温度参数", example = "1") + private Double temperature; + + @Schema(description = "单条回复的最大 Token 数量", example = "4096") + private Integer maxTokens; + + @Schema(description = "上下文的最大 Message 数量", example = "8192") + private Integer maxContexts; + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java deleted file mode 100644 index 28a439733..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalRespVO.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.Size; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * modal list - * - * @author fansili - * @time 2024/4/24 19:56 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalRespVO { - - @Schema(description = "编号") - private Long id; - - @Schema(description = "API 秘钥编号") - private Long keyId; - - @Schema(description = "模型名字") - private String name; - - @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)") - private String model; - - @Size(max = 32, message = "模型平台最大32个字符") - private String platform; - - @Schema(description = "排序") - private Integer sort; - - @Schema(description = "状态") - private Integer status; - - // ========== 会话配置 ========== - - @Schema(description = "温度参数") - private Integer temperature; - - @Schema(description = "单条回复的最大 Token 数量") - private Integer maxTokens; - - @Schema(description = "上下文的最大 Message 数量") - private Integer maxContexts; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java deleted file mode 100644 index 4e9fd898c..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModalUpdateReqVO.java +++ /dev/null @@ -1,63 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * ai chat modal - * - * @author fansili - * @time 2024/4/24 19:47 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModalUpdateReqVO { - - @Schema(description = "编号") - @Size(max = 32, message = "编号最大32个字符") - @NotNull(message = "编号不能为空") - private Long id; - - @Schema(description = "API 秘钥编号") - @Size(max = 32, message = "API 秘钥编号最大32个字符") - @NotNull(message = "API 秘钥编号不能为空!") - private Long keyId; - - @Schema(description = "模型名字") - @Size(max = 60, message = "模型名字最大60个字符") - @NotNull(message = "模型名字不能为空!") - private String name; - - @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)") - @Size(max = 32, message = "模型类型最大32个字符") - @NotNull(message = "model模型不能为空!") - private String model; - - @Size(max = 32, message = "模型平台最大32个字符") - @Schema(description = "模型平台 参考 AiPlatformEnum") - @NotNull(message = "平台不能为空!") - private String platform; - - @Schema(description = "排序") - @NotNull(message = "sort排序不能为空!") - private Integer sort; - - @Schema(description = "状态") - @NotNull(message = "状态不能为空!") - private Integer status; - - // ========== 会话配置 ========== - - @Schema(description = "温度参数") - private Integer temperature; - - @Schema(description = "单条回复的最大 Token 数量") - private Integer maxTokens; - - @Schema(description = "上下文的最大 Message 数量") - private Integer maxContexts; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java deleted file mode 100644 index 569760fab..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelAddReqVO.java +++ /dev/null @@ -1,53 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * ai chat modal - * - * @author fansili - * @time 2024/4/24 19:47 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModelAddReqVO { - - @Schema(description = "API 秘钥编号") - @NotNull(message = "API 秘钥编号不能为空!") - private Long keyId; - - @Schema(description = "模型名字") - @Size(max = 60, message = "模型名字最大60个字符") - @NotNull(message = "模型名字不能为空!") - private String name; - - @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)") - @Size(max = 32, message = "模型类型最大32个字符") - @NotNull(message = "model模型不能为空!") - private String model; - - @Size(max = 32, message = "模型平台最大32个字符") - @Schema(description = "模型平台 参考 AiPlatformEnum") - @NotNull(message = "平台不能为空!") - private String platform; - - @Schema(description = "排序") - @NotNull(message = "sort排序不能为空!") - private Integer sort; - - // ========== 会话配置 ========== - - @Schema(description = "温度参数") - private Integer temperature; - - @Schema(description = "单条回复的最大 Token 数量") - private Integer maxTokens; - - @Schema(description = "上下文的最大 Message 数量") - private Integer maxContexts; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java deleted file mode 100644 index 7e3cfff13..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListReqVO.java +++ /dev/null @@ -1,22 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import cn.iocoder.yudao.framework.common.pojo.PageParam; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * modal list - * - * @author fansili - * @time 2024/4/24 19:56 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModelListReqVO extends PageParam { - - @Schema(description = "名字搜搜") - private String search; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java deleted file mode 100644 index 916106a23..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelListRespVO.java +++ /dev/null @@ -1,50 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * modal list - * - * @author fansili - * @time 2024/4/24 19:56 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModelListRespVO { - - @Schema(description = "编号") - private Long id; - - @Schema(description = "API 秘钥编号") - private Long keyId; - - @Schema(description = "模型名称") - private String name; - - @Schema(description = "模型标志") - private String model; - - @Schema(description = "平台") - private String platform; - - @Schema(description = "排序值") - private Integer sort; - - @Schema(description = "状态") - private Integer status; - - // ========== 会话配置 ========== - - @Schema(description = "温度参数") - private Double temperature; - - @Schema(description = "单条回复的最大 Token 数量") - private Integer maxTokens; - - @Schema(description = "上下文的最大 Message 数量") - private Integer maxContexts; - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java deleted file mode 100644 index 577e998e9..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/model/vo/model/AiChatModelUpdateReqVO.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.ai.controller.admin.model.vo.model; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotNull; -import jakarta.validation.constraints.Size; -import lombok.Data; -import lombok.experimental.Accessors; - -/** - * ai chat modal - * - * @author fansili - * @time 2024/4/24 19:47 - * @since 1.0 - */ -@Data -@Accessors(chain = true) -public class AiChatModelUpdateReqVO { - - @Schema(description = "编号") - @NotNull(message = "编号不能为空") - private Long id; - - @Schema(description = "API 秘钥编号") - @NotNull(message = "API 秘钥编号不能为空!") - private Long keyId; - - @Schema(description = "模型名字") - @Size(max = 60, message = "模型名字最大60个字符") - @NotNull(message = "模型名字不能为空!") - private String name; - - @Schema(description = "模型类型(qianwen、yiyan、xinghuo、openai)") - @Size(max = 32, message = "模型类型最大32个字符") - @NotNull(message = "model模型不能为空!") - private String model; - - @Size(max = 32, message = "模型平台最大32个字符") - @Schema(description = "模型平台 参考 AiPlatformEnum") - @NotNull(message = "平台不能为空!") - private String platform; - - @Schema(description = "排序") - @NotNull(message = "sort排序不能为空!") - private Integer sort; - - // ========== 会话配置 ========== - - @Schema(description = "温度参数") - private Integer temperature; - - @Schema(description = "单条回复的最大 Token 数量") - private Integer maxTokens; - - @Schema(description = "上下文的最大 Message 数量") - private Integer maxContexts; -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java index d067a26f3..a5019b2cd 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatMessageConvert.java @@ -19,14 +19,6 @@ public interface AiChatMessageConvert { AiChatMessageConvert INSTANCE = Mappers.getMapper(AiChatMessageConvert.class); - /** - * 转换 ChatMessageListRes - * - * @param list - * @return - */ - List convert(List list); - /** * 转换 AiChatMessageRespVO * diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java deleted file mode 100644 index d6d57b5ef..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/convert/AiChatModelConvert.java +++ /dev/null @@ -1,57 +0,0 @@ -package cn.iocoder.yudao.module.ai.convert; - -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelAddReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelListRespVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModelUpdateReqVO; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import org.mapstruct.Mapper; -import org.mapstruct.factory.Mappers; - -import java.util.List; - -/** - * 聊天 modal - * - * @author fansili - * @time 2024/4/18 16:39 - * @since 1.0 - */ -@Mapper -public interface AiChatModelConvert { - - AiChatModelConvert INSTANCE = Mappers.getMapper(AiChatModelConvert.class); - - /** - * 转换 - AiChatModalListRes - * - * @param list - * @return - */ - List convertAiChatModalListRes(List list); - - /** - * 转换 - AiChatModalDO - * - * @param req - * @return - */ - AiChatModelDO convertAiChatModalDO(AiChatModelAddReqVO req); - - /** - * 转换 - AiChatModalDO - * - * @param req - * @return - */ - AiChatModelDO convertAiChatModalDO(AiChatModelUpdateReqVO req); - - /** - * 转换 - AiChatModalRes - * - * @param aiChatModalDO - * @return - */ - AiChatModalRespVO convertAiChatModalRes(AiChatModelDO aiChatModalDO); - -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java index 46d66ff5f..c2d75a218 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/AiChatModelMapper.java @@ -5,21 +5,19 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import org.apache.ibatis.annotations.Mapper; -import org.springframework.stereotype.Repository; /** - * chat modal + * API 聊天模型 Mapper * * @author fansili - * @time 2024/4/24 19:41 - * @since 1.0 */ -@Repository @Mapper public interface AiChatModelMapper extends BaseMapperX { + // TODO 芋艿:要搞一下 /** * 查询 - 第一个modal * @@ -36,5 +34,12 @@ public interface AiChatModelMapper extends BaseMapperX { return pageResult.getList().get(0); } + default PageResult selectPage(AiChatModelPageReqVO reqVO) { + return selectPage(reqVO, new LambdaQueryWrapperX() + .likeIfPresent(AiChatModelDO::getName, reqVO.getName()) + .eqIfPresent(AiChatModelDO::getModel, reqVO.getModel()) + .eqIfPresent(AiChatModelDO::getPlatform, reqVO.getPlatform()) + .orderByDesc(AiChatModelDO::getId)); + } } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java deleted file mode 100644 index 07a902dd8..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatModelService.java +++ /dev/null @@ -1,67 +0,0 @@ -package cn.iocoder.yudao.module.ai.service; - -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.*; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; - -/** - * ai modal - * - * @author fansili - * @time 2024/4/24 19:42 - * @since 1.0 - */ -public interface AiChatModelService { - - /** - * ai modal - 列表 - * - * @param req - * @return - */ - PageResult list(AiChatModelListReqVO req); - - /** - * ai modal - 添加 - * - * @param req - */ - void add(AiChatModelAddReqVO req); - - /** - * ai modal - 更新 - * - * @param req - */ - void update(AiChatModelUpdateReqVO req); - - /** - * ai modal - 删除 - * - * @param id - */ - void delete(Long id); - - /** - * 获取 - 获取 modal - * - * @param modalId - * @return - */ - AiChatModalRespVO getChatModalOfValidate(Long modalId); - - /** - * 校验 - 是否存在 - * - * @param id - * @return - */ - AiChatModelDO validateExists(Long id); - - /** - * 校验 - 校验是否可用 - * - * @param chatModal - */ - void validateAvailable(AiChatModalRespVO chatModal); -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java index 96d722100..c2f56a240 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/AiChatRoleService.java @@ -72,4 +72,5 @@ public interface AiChatRoleService { * @param aiChatRoleDO */ void validateIsPublic(AiChatRoleDO aiChatRoleDO); + } diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java deleted file mode 100644 index 0aec7a94c..000000000 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatModalServiceImpl.java +++ /dev/null @@ -1,127 +0,0 @@ -package cn.iocoder.yudao.module.ai.service.impl; - -import cn.hutool.core.util.StrUtil; -import cn.hutool.extra.validation.ValidationUtil; -import cn.iocoder.yudao.framework.ai.AiPlatformEnum; -import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; -import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; -import cn.iocoder.yudao.framework.common.pojo.PageResult; -import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; -import cn.iocoder.yudao.module.ai.ErrorCodeConstants; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.*; -import cn.iocoder.yudao.module.ai.convert.AiChatModelConvert; -import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; -import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; -import cn.iocoder.yudao.module.ai.dal.vo.AiChatModalConfigVO; -import cn.iocoder.yudao.module.ai.service.AiChatModelService; -import jakarta.validation.ConstraintViolation; -import lombok.AllArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; - -import java.util.List; -import java.util.Set; - -/** - * ai 模型 - * - * @author fansili - * @time 2024/4/24 19:42 - * @since 1.0 - */ -@AllArgsConstructor -@Service -@Slf4j -public class AiChatModalServiceImpl implements AiChatModelService { - - private final AiChatModelMapper aiChatModelMapper; - - @Override - public PageResult list(AiChatModelListReqVO req) { - LambdaQueryWrapperX queryWrapperX = new LambdaQueryWrapperX<>(); - // 查询的都是未禁用的模型 - queryWrapperX.eq(AiChatModelDO::getStatus, CommonStatusEnum.ENABLE.getStatus()); - // search - if (!StrUtil.isBlank(req.getSearch())) { - queryWrapperX.like(AiChatModelDO::getName, req.getSearch().trim()); - } - // 默认排序 - queryWrapperX.orderByAsc(AiChatModelDO::getSort); - // 查询 - PageResult aiChatModalDOPageResult = aiChatModelMapper.selectPage(req, queryWrapperX); - // 转换 res - List resList = AiChatModelConvert.INSTANCE.convertAiChatModalListRes(aiChatModalDOPageResult.getList()); - return new PageResult<>(resList, aiChatModalDOPageResult.getTotal()); - } - - @Override - public void add(AiChatModelAddReqVO req) { - // 校验 platform、type - validatePlatform(req.getPlatform()); - // 转换 do - AiChatModelDO insertChatModalDO = AiChatModelConvert.INSTANCE.convertAiChatModalDO(req); - // 设置默认属性 - insertChatModalDO.setStatus(CommonStatusEnum.ENABLE.getStatus()); - // 保存数据库 - aiChatModelMapper.insert(insertChatModalDO); - } - - @Override - public void update(AiChatModelUpdateReqVO req) { - // 校验 platform - validatePlatform(req.getPlatform()); - // 校验模型是否存在 - validateExists(req.getId()); - // 转换 updateChatModalDO - AiChatModelDO updateChatModalDO = AiChatModelConvert.INSTANCE.convertAiChatModalDO(req); - updateChatModalDO.setId(req.getId()); - // 更新数据库 - aiChatModelMapper.updateById(updateChatModalDO); - } - - @Override - public void delete(Long id) { - // 检查 modal 是否存在 - validateExists(id); - // 删除 delete - aiChatModelMapper.deleteById(id); - } - - @Override - public AiChatModalRespVO getChatModalOfValidate(Long modalId) { - // 检查 modal 是否存在 - AiChatModelDO aiChatModalDO = validateExists(modalId); - return AiChatModelConvert.INSTANCE.convertAiChatModalRes(aiChatModalDO); - } - - @Override - public void validateAvailable(AiChatModalRespVO chatModal) { - // 对话模型是否可用 - if (!CommonStatusEnum.ENABLE.getStatus().equals(chatModal.getStatus())) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_DISABLE_NOT_USED); - } - } - - public AiChatModelDO validateExists(Long id) { - AiChatModelDO aiChatModalDO = aiChatModelMapper.selectById(id); - if (aiChatModalDO == null) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_NOT_EXIST); - } - return aiChatModalDO; - } - - private void validatePlatform(String platform) { - try { - AiPlatformEnum.valueOfPlatform(platform); - } catch (IllegalArgumentException e) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_PLATFORM_PARAMS_INCORRECT, e.getMessage()); - } - } - - private void validateModalConfig(AiChatModalConfigVO aiChatModalConfigVO) { - Set> validate = ValidationUtil.validate(aiChatModalConfigVO); - for (ConstraintViolation constraintViolation : validate) { - throw ServiceExceptionUtil.exception(ErrorCodeConstants.AI_MODAL_CONFIG_PARAMS_INCORRECT, constraintViolation.getMessage()); - } - } -} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java index cc432fe41..426481495 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatRoleServiceImpl.java @@ -12,7 +12,7 @@ import cn.iocoder.yudao.module.ai.convert.AiChatRoleConvert; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatRoleMapper; import cn.iocoder.yudao.module.ai.enums.AiChatRoleCategoryEnum; -import cn.iocoder.yudao.module.ai.service.AiChatModelService; +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import cn.iocoder.yudao.module.ai.service.AiChatRoleService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -57,8 +57,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { public void add(AiChatRoleAddReqVO req) { // 转换enum,并校验enum AiChatRoleCategoryEnum.valueOfCategory(req.getCategory()); - // 校验模型是否存在 - aiChatModalService.validateExists(req.getModelId()); + // 校验模型是否存在 TODO +// aiChatModalService.validateExists(req.getModelId()); // 转换do AiChatRoleDO insertAiChatRoleDO = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req); insertAiChatRoleDO.setUserId(SecurityFrameworkUtils.getLoginUserId()); @@ -73,8 +73,8 @@ public class AiChatRoleServiceImpl implements AiChatRoleService { validateExists(req.getId()); // 转换enum,并校验enum AiChatRoleCategoryEnum.valueOfCategory(req.getCategory()); - // 校验模型是否存在 - aiChatModalService.validateExists(req.getModelId()); + // 校验模型是否存在 TODO +// aiChatModalService.validateExists(req.getModelId()); // 转换do AiChatRoleDO updateChatRole = AiChatRoleConvert.INSTANCE.convertAiChatRoleDO(req); updateChatRole.setId(req.getId()); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java index 90649f417..be8f81d72 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatServiceImpl.java @@ -12,14 +12,14 @@ import cn.iocoder.yudao.module.ai.config.AiChatClientFactory; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.message.AiChatMessageSendReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO; import cn.iocoder.yudao.module.ai.convert.AiChatMessageConvert; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatMessageDO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatRoleDO; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatMessageMapper; import cn.iocoder.yudao.module.ai.service.AiChatConversationService; -import cn.iocoder.yudao.module.ai.service.AiChatModelService; +import cn.iocoder.yudao.module.ai.service.model.AiChatModelService; import cn.iocoder.yudao.module.ai.service.AiChatRoleService; import cn.iocoder.yudao.module.ai.service.AiChatService; import lombok.AllArgsConstructor; @@ -57,9 +57,7 @@ public class AiChatServiceImpl implements AiChatService { // 查询对话 AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); // 获取对话模型 - AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId()); - // 对话模型是否可用 - aiChatModalService.validateAvailable(chatModal); + AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); // 获取角色信息 AiChatRoleDO aiChatRoleDO = null; if (conversation.getRoleId() != null) { @@ -68,10 +66,10 @@ public class AiChatServiceImpl implements AiChatService { // 校验角色是否公开 aiChatRoleService.validateIsPublic(aiChatRoleDO); // 获取 client 类型 - AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform()); + AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); // 保存 chat message insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), - chatModal.getModel(), chatModal.getId(), req.getContent(), + chatModel.getModel(), chatModel.getId(), req.getContent(), null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); String content = null; int tokens = 0; @@ -93,7 +91,7 @@ public class AiChatServiceImpl implements AiChatService { } finally { // 保存 chat message insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(), - chatModal.getModel(), chatModal.getId(), content, + chatModel.getModel(), chatModel.getId(), content, tokens, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); } return new AiChatMessageRespVO().setContent(content); @@ -128,9 +126,7 @@ public class AiChatServiceImpl implements AiChatService { // 查询对话 AiChatConversationRespVO conversation = chatConversationService.getConversationOfValidate(req.getConversationId()); // 获取对话模型 - AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(conversation.getModelId()); - // 对话模型是否可用 - aiChatModalService.validateAvailable(chatModal); + AiChatModelDO chatModel = aiChatModalService.validateChatModel(conversation.getModelId()); // 获取角色信息 AiChatRoleDO aiChatRoleDO = null; if (conversation.getRoleId() != null) { @@ -145,10 +141,10 @@ public class AiChatServiceImpl implements AiChatService { // req.setTemperature(req.getTemperature()); // 保存 chat message insertChatMessage(conversation.getId(), MessageType.USER, loginUserId, conversation.getRoleId(), - chatModal.getModel(), chatModal.getId(), req.getContent(), + chatModel.getModel(), chatModel.getId(), req.getContent(), null, conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); // 获取 client 类型 - AiPlatformEnum platformEnum = AiPlatformEnum.valueOfPlatform(chatModal.getPlatform()); + AiPlatformEnum platformEnum = AiPlatformEnum.validatePlatform(chatModel.getPlatform()); StreamingChatClient streamingChatClient = aiChatClientFactory.getStreamingChatClient(platformEnum); Flux streamResponse = streamingChatClient.stream(prompt); // 转换 flex AiChatMessageRespVO @@ -167,7 +163,7 @@ public class AiChatServiceImpl implements AiChatService { log.info("发送完成!"); // 保存 chat message insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(), - chatModal.getModel(), chatModal.getId(), contentBuffer.toString(), + chatModel.getModel(), chatModel.getId(), contentBuffer.toString(), tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); } }).doOnError(new Consumer() { @@ -176,7 +172,7 @@ public class AiChatServiceImpl implements AiChatService { log.error("发送错误 {}!", throwable.getMessage()); // 保存 chat message insertChatMessage(conversation.getId(), MessageType.SYSTEM, loginUserId, conversation.getRoleId(), - chatModal.getModel(), chatModal.getId(), throwable.getMessage(), + chatModel.getModel(), chatModel.getId(), throwable.getMessage(), tokens.get(), conversation.getTemperature(), conversation.getMaxTokens(), conversation.getMaxContexts()); } }); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java index 7390bc8e6..4da3c3500 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyService.java @@ -14,7 +14,7 @@ import jakarta.validation.Valid; public interface AiApiKeyService { /** - * 创建AI API 密钥 + * 创建 API 密钥 * * @param createReqVO 创建信息 * @return 编号 @@ -22,32 +22,40 @@ public interface AiApiKeyService { Long createApiKey(@Valid AiApiKeySaveReqVO createReqVO); /** - * 更新AI API 密钥 + * 更新 API 密钥 * * @param updateReqVO 更新信息 */ void updateApiKey(@Valid AiApiKeySaveReqVO updateReqVO); /** - * 删除AI API 密钥 + * 删除 API 密钥 * * @param id 编号 */ void deleteApiKey(Long id); /** - * 获得AI API 密钥 + * 获得 API 密钥 * * @param id 编号 - * @return AI API 密钥 + * @return API 密钥 */ AiApiKeyDO getApiKey(Long id); /** - * 获得AI API 密钥分页 + * 校验 API 密钥 + * + * @param id 比那好 + * @return API 密钥 + */ + AiApiKeyDO validateApiKey(Long id); + + /** + * 获得 API 密钥分页 * * @param pageReqVO 分页查询 - * @return AI API 密钥分页 + * @return API 密钥分页 */ PageResult getApiKeyPage(AiApiKeyPageReqVO pageReqVO); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java index 9bafb17d5..06e68748f 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiApiKeyServiceImpl.java @@ -1,5 +1,6 @@ package cn.iocoder.yudao.module.ai.service.model; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.apikey.AiApiKeyPageReqVO; @@ -11,7 +12,7 @@ import org.springframework.stereotype.Service; import org.springframework.validation.annotation.Validated; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; -import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.API_KEY_NOT_EXISTS; +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; /** * AI API 密钥 Service 实现类 @@ -51,10 +52,12 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { apiKeyMapper.deleteById(id); } - private void validateApiKeyExists(Long id) { - if (apiKeyMapper.selectById(id) == null) { + private AiApiKeyDO validateApiKeyExists(Long id) { + AiApiKeyDO apiKey = apiKeyMapper.selectById(id); + if (apiKey == null) { throw exception(API_KEY_NOT_EXISTS); } + return apiKey; } @Override @@ -62,6 +65,15 @@ public class AiApiKeyServiceImpl implements AiApiKeyService { return apiKeyMapper.selectById(id); } + @Override + public AiApiKeyDO validateApiKey(Long id) { + AiApiKeyDO apiKey = validateApiKeyExists(id); + if (CommonStatusEnum.isDisable(apiKey.getStatus())) { + throw exception(API_KEY_DISABLE); + } + return apiKey; + } + @Override public PageResult getApiKeyPage(AiApiKeyPageReqVO pageReqVO) { return apiKeyMapper.selectPage(pageReqVO); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java similarity index 93% rename from yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java rename to yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java index dc5205011..5d25f2a11 100644 --- a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/impl/AiChatConversationServiceImpl.java +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatConversationServiceImpl.java @@ -1,4 +1,4 @@ -package cn.iocoder.yudao.module.ai.service.impl; +package cn.iocoder.yudao.module.ai.service.model; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils; @@ -7,7 +7,6 @@ import cn.iocoder.yudao.module.ai.ErrorCodeConstants; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationCreateReqVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationRespVO; import cn.iocoder.yudao.module.ai.controller.admin.chat.vo.conversation.AiChatConversationUpdateReqVO; -import cn.iocoder.yudao.module.ai.controller.admin.model.vo.model.AiChatModalRespVO; import cn.iocoder.yudao.module.ai.controller.admin.model.vo.role.AiChatRoleRespVO; import cn.iocoder.yudao.module.ai.convert.AiChatConversationConvert; import cn.iocoder.yudao.module.ai.dal.dataobject.chat.AiChatConversationDO; @@ -15,7 +14,6 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatConversationMapper; import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; import cn.iocoder.yudao.module.ai.service.AiChatConversationService; -import cn.iocoder.yudao.module.ai.service.AiChatModelService; import cn.iocoder.yudao.module.ai.service.AiChatRoleService; import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -87,9 +85,7 @@ public class AiChatConversationServiceImpl implements AiChatConversationService // 校验对话是否存在 validateExists(updateReqVO.getId()); // 获取模型信息并验证 - AiChatModalRespVO chatModal = aiChatModalService.getChatModalOfValidate(updateReqVO.getModelId()); - // 校验modal是否可用 - aiChatModalService.validateAvailable(chatModal); + aiChatModalService.validateChatModel(updateReqVO.getModelId()); // 更新对话信息 AiChatConversationDO updateAiChatConversationDO = AiChatConversationConvert.INSTANCE.convertAiChatConversationDO(updateReqVO); diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java new file mode 100644 index 000000000..d05941989 --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelService.java @@ -0,0 +1,63 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import jakarta.validation.Valid; + +/** + * AI 聊天模型 Service 接口 + * + * @author fansili + * @since 2024/4/24 19:42 + */ +public interface AiChatModelService { + + /** + * 创建聊天模型 + * + * @param createReqVO 创建信息 + * @return 编号 + */ + Long createChatModel(@Valid AiChatModelSaveReqVO createReqVO); + + /** + * 更新聊天模型 + * + * @param updateReqVO 更新信息 + */ + void updateChatModel(@Valid AiChatModelSaveReqVO updateReqVO); + + /** + * 删除聊天模型 + * + * @param id 编号 + */ + void deleteChatModel(Long id); + + /** + * 获得聊天模型 + * + * @param id 编号 + * @return API 聊天模型 + */ + AiChatModelDO getChatModel(Long id); + + /** + * 获得聊天模型分页 + * + * @param pageReqVO 分页查询 + * @return API 聊天模型分页 + */ + PageResult getChatModelPage(AiChatModelPageReqVO pageReqVO); + + /** + * 校验聊天模型 + * + * @param id 编号 + * @return 聊天模型 + */ + AiChatModelDO validateChatModel(Long id); + +} diff --git a/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java new file mode 100644 index 000000000..0c0386ccc --- /dev/null +++ b/yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiChatModelServiceImpl.java @@ -0,0 +1,92 @@ +package cn.iocoder.yudao.module.ai.service.model; + +import cn.iocoder.yudao.framework.ai.AiPlatformEnum; +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; +import cn.iocoder.yudao.framework.common.pojo.PageResult; +import cn.iocoder.yudao.framework.common.util.object.BeanUtils; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelPageReqVO; +import cn.iocoder.yudao.module.ai.controller.admin.model.vo.chatModel.AiChatModelSaveReqVO; +import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; +import cn.iocoder.yudao.module.ai.dal.mysql.AiChatModelMapper; +import jakarta.annotation.Resource; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; +import static cn.iocoder.yudao.module.ai.ErrorCodeConstants.*; + +/** + * AI 聊天模型 Service 实现类 + * + * @author fansili + */ +@Service +@Validated +public class AiChatModelServiceImpl implements AiChatModelService { + + @Resource + private AiApiKeyService apiKeyService; + + @Resource + private AiChatModelMapper chatModelMapper; + + @Override + public Long createChatModel(AiChatModelSaveReqVO createReqVO) { + // 1. 校验 + AiPlatformEnum.validatePlatform(createReqVO.getPlatform()); + apiKeyService.validateApiKey(createReqVO.getKeyId()); + + // 2. 插入 + AiChatModelDO chatModel = BeanUtils.toBean(createReqVO, AiChatModelDO.class); + chatModelMapper.insert(chatModel); + return chatModel.getId(); + } + + @Override + public void updateChatModel(AiChatModelSaveReqVO updateReqVO) { + // 1. 校验 + validateChatModelExists(updateReqVO.getId()); + AiPlatformEnum.validatePlatform(updateReqVO.getPlatform()); + apiKeyService.validateApiKey(updateReqVO.getKeyId()); + + // 2. 更新 + AiChatModelDO updateObj = BeanUtils.toBean(updateReqVO, AiChatModelDO.class); + chatModelMapper.updateById(updateObj); + } + + @Override + public void deleteChatModel(Long id) { + // 校验存在 + validateChatModelExists(id); + // 删除 + chatModelMapper.deleteById(id); + } + + private AiChatModelDO validateChatModelExists(Long id) { + AiChatModelDO model = chatModelMapper.selectById(id); + if (chatModelMapper.selectById(id) == null) { + throw exception(CHAT_MODAL_NOT_EXIST); + } + return model; + } + + @Override + public AiChatModelDO getChatModel(Long id) { + return chatModelMapper.selectById(id); + } + + @Override + public PageResult getChatModelPage(AiChatModelPageReqVO pageReqVO) { + return chatModelMapper.selectPage(pageReqVO); + } + + @Override + public AiChatModelDO validateChatModel(Long id) { + AiChatModelDO model = validateChatModelExists(id); + if (CommonStatusEnum.isDisable(model.getStatus())) { + throw exception(CHAT_MODAL_DISABLE); + } + return model; + } + +} \ No newline at end of file diff --git a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java index 427202391..c774d2515 100644 --- a/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java +++ b/yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/AiPlatformEnum.java @@ -41,7 +41,7 @@ public enum AiPlatformEnum { AiPlatformEnum.MIDJOURNEY ); - public static AiPlatformEnum valueOfPlatform(String platform) { + public static AiPlatformEnum validatePlatform(String platform) { for (AiPlatformEnum itemEnum : AiPlatformEnum.values()) { if (itemEnum.getPlatform().equals(platform)) { return itemEnum;