Commit 098483d2beb7d72d44c2f7e349199d3ed9230673
1 parent
88142ed7
【代码优化】AI:MJ 生成图片的优化
Showing
23 changed files
with
322 additions
and
642 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
| @@ -21,30 +21,21 @@ public interface ErrorCodeConstants { | @@ -21,30 +21,21 @@ public interface ErrorCodeConstants { | ||
| 21 | // ========== API 聊天模型 1-040-002-000 ========== | 21 | // ========== API 聊天模型 1-040-002-000 ========== |
| 22 | ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); | 22 | ErrorCode CHAT_ROLE_NOT_EXISTS = new ErrorCode(1_040_002_000, "聊天角色不存在"); |
| 23 | ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); | 23 | ErrorCode CHAT_ROLE_DISABLE = new ErrorCode(1_040_001_001, "聊天角色({})已禁用!"); |
| 24 | - ErrorCode CHAT_ROLE_DEFAULT_NOT_EXISTS = new ErrorCode(1_040_001_002, "操作失败,找不到默认聊天角色"); | ||
| 25 | 24 | ||
| 26 | // ========== API 聊天会话 1-040-003-000 ========== | 25 | // ========== API 聊天会话 1-040-003-000 ========== |
| 27 | 26 | ||
| 28 | ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!"); | 27 | ErrorCode CHAT_CONVERSATION_NOT_EXISTS = new ErrorCode(1_040_003_000, "对话不存在!"); |
| 29 | ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); | 28 | ErrorCode CHAT_CONVERSATION_MODEL_ERROR = new ErrorCode(1_040_003_001, "操作失败,该聊天模型的配置不完整"); |
| 30 | - ErrorCode CHAT_CONVERSATION_UPDATE_MAX_TOKENS_ERROR = new ErrorCode(1_040_003_002, "更新对话失败,最大 Token 超过上限"); | ||
| 31 | - ErrorCode CHAT_CONVERSATION_UPDATE_MAX_CONTEXTS_ERROR = new ErrorCode(1_040_003_002, "更新对话失败,最大 Context 超过上限"); | ||
| 32 | 29 | ||
| 33 | // ========== API 聊天消息 1-040-004-000 ========== | 30 | // ========== API 聊天消息 1-040-004-000 ========== |
| 31 | + | ||
| 34 | ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!"); | 32 | ErrorCode AI_CHAT_MESSAGE_NOT_EXIST = new ErrorCode(1_040_004_000, "消息不存在!"); |
| 35 | ErrorCode AI_CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "Stream 对话异常!"); | 33 | ErrorCode AI_CHAT_STREAM_ERROR = new ErrorCode(1_040_004_001, "Stream 对话异常!"); |
| 36 | 34 | ||
| 37 | - // midjourney | ||
| 38 | - | ||
| 39 | - ErrorCode AI_MIDJOURNEY_IMAGINE_FAIL = new ErrorCode(1_022_000_040, "midjourney imagine 操作失败!"); | ||
| 40 | - ErrorCode AI_MIDJOURNEY_OPERATION_NOT_EXISTS = new ErrorCode(1_022_000_040, "midjourney 操作不存在!"); | ||
| 41 | - ErrorCode AI_MIDJOURNEY_MESSAGE_ID_INCORRECT = new ErrorCode(1_022_000_040, "midjourney message id 不正确!"); | ||
| 42 | - | ||
| 43 | // ========== API 绘画 1-040-005-000 ========== | 35 | // ========== API 绘画 1-040-005-000 ========== |
| 44 | 36 | ||
| 45 | - ErrorCode AI_IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "image 不存在!"); | ||
| 46 | - ErrorCode AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败! {}"); | 37 | + ErrorCode AI_IMAGE_NOT_EXISTS = new ErrorCode(1_022_005_000, "图片不存在!"); |
| 38 | + ErrorCode AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL = new ErrorCode(1_022_005_001, "Midjourney 提交失败!原因:{}"); | ||
| 47 | ErrorCode AI_IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); | 39 | ErrorCode AI_IMAGE_CUSTOM_ID_NOT_EXISTS = new ErrorCode(1_022_005_002, "Midjourney 按钮 customId 不存在! {}"); |
| 48 | - ErrorCode AI_IMAGE_SYSTEM_ACCOUNT_INSUFFICIENT_BALANCE = new ErrorCode(1_022_005_003, "Midjourney 系统账户余额不足!"); | ||
| 49 | 40 | ||
| 50 | } | 41 | } |
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/image/AiImageStatusEnum.java
| @@ -12,20 +12,20 @@ import lombok.Getter; | @@ -12,20 +12,20 @@ import lombok.Getter; | ||
| 12 | @Getter | 12 | @Getter |
| 13 | public enum AiImageStatusEnum { | 13 | public enum AiImageStatusEnum { |
| 14 | 14 | ||
| 15 | - IN_PROGRESS("10", "进行中"), | ||
| 16 | - SUCCESS("20", "完成"), | ||
| 17 | - FAIL("30", "失败"); | 15 | + IN_PROGRESS(10, "进行中"), |
| 16 | + SUCCESS(20, "完成"), | ||
| 17 | + FAIL(30, "失败"); | ||
| 18 | 18 | ||
| 19 | /** | 19 | /** |
| 20 | * 状态 | 20 | * 状态 |
| 21 | */ | 21 | */ |
| 22 | - private final String status; | 22 | + private final Integer status; |
| 23 | /** | 23 | /** |
| 24 | * 状态名 | 24 | * 状态名 |
| 25 | */ | 25 | */ |
| 26 | private final String name; | 26 | private final String name; |
| 27 | 27 | ||
| 28 | - public static AiImageStatusEnum valueOfStatus(String status) { | 28 | + public static AiImageStatusEnum valueOfStatus(Integer status) { |
| 29 | for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) { | 29 | for (AiImageStatusEnum statusEnum : AiImageStatusEnum.values()) { |
| 30 | if (statusEnum.getStatus().equals(status)) { | 30 | if (statusEnum.getStatus().equals(status)) { |
| 31 | return statusEnum; | 31 | return statusEnum; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/config/YudaoMidjourneyConfiguration.java
| @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.ai.config; | @@ -3,7 +3,6 @@ package cn.iocoder.yudao.module.ai.config; | ||
| 3 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.MidjourneyConfig; | 3 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.MidjourneyConfig; |
| 4 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | 4 | import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; |
| 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 5 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 6 | -import org.springframework.boot.autoconfigure.AutoConfiguration; | ||
| 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; | 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
| 8 | import org.springframework.context.annotation.Bean; | 7 | import org.springframework.context.annotation.Bean; |
| 9 | import org.springframework.context.annotation.Configuration; | 8 | import org.springframework.context.annotation.Configuration; |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.http
| @@ -29,12 +29,17 @@ Authorization: {{token}} | @@ -29,12 +29,17 @@ Authorization: {{token}} | ||
| 29 | "style": "vivid" | 29 | "style": "vivid" |
| 30 | } | 30 | } |
| 31 | 31 | ||
| 32 | -### chat midjourney | 32 | +### 生成图片:生成图片 |
| 33 | 33 | ||
| 34 | -POST {{baseUrl}}/admin-api/ai/image/midjourney | 34 | +POST {{baseUrl}}/ai/image/midjourney/imagine |
| 35 | Content-Type: application/json | 35 | Content-Type: application/json |
| 36 | Authorization: {{token}} | 36 | Authorization: {{token}} |
| 37 | 37 | ||
| 38 | { | 38 | { |
| 39 | - "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." | 39 | + "prompt": "中国旗袍", |
| 40 | + "model": "midjourney", | ||
| 41 | + "width": "1", | ||
| 42 | + "height": "1", | ||
| 43 | + "version": "6.0", | ||
| 44 | + "base64Array": [] | ||
| 40 | } | 45 | } |
| 41 | \ No newline at end of file | 46 | \ No newline at end of file |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/AiImageController.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.image; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.image; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.util.ObjUtil; | 3 | import cn.hutool.core.util.ObjUtil; |
| 4 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 6 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 7 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 8 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 8 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.MidjourneyNotifyReqVO; | ||
| 9 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; | 9 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; |
| 10 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; | ||
| 11 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO; | 10 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageRespVO; |
| 11 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiImageMidjourneyImagineReqVO; | ||
| 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 12 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 13 | import cn.iocoder.yudao.module.ai.service.image.AiImageService; | 13 | import cn.iocoder.yudao.module.ai.service.image.AiImageService; |
| 14 | import io.swagger.v3.oas.annotations.Operation; | 14 | import io.swagger.v3.oas.annotations.Operation; |
| @@ -63,19 +63,24 @@ public class AiImageController { | @@ -63,19 +63,24 @@ public class AiImageController { | ||
| 63 | return success(true); | 63 | return success(true); |
| 64 | } | 64 | } |
| 65 | 65 | ||
| 66 | - // ================ midjourney 接口 ================ | 66 | + // ================ midjourney 专属 ================ |
| 67 | 67 | ||
| 68 | - @Operation(summary = "Midjourney imagine(绘画)") | 68 | + @Operation(summary = "【Midjourney】生成图片") |
| 69 | @PostMapping("/midjourney/imagine") | 69 | @PostMapping("/midjourney/imagine") |
| 70 | - public CommonResult<Long> midjourneyImagine(@Validated @RequestBody AiImageMidjourneyImagineReqVO req) { | ||
| 71 | - return success(imageService.midjourneyImagine(getLoginUserId(), req)); | 70 | + public CommonResult<Long> midjourneyImagine(@Validated @RequestBody AiImageMidjourneyImagineReqVO reqVO) { |
| 71 | + if (true) { | ||
| 72 | + imageService.midjourneySync(); | ||
| 73 | + return null; | ||
| 74 | + } | ||
| 75 | + Long imageId = imageService.midjourneyImagine(getLoginUserId(), reqVO); | ||
| 76 | + return success(imageId); | ||
| 72 | } | 77 | } |
| 73 | 78 | ||
| 74 | - @Operation(summary = "Midjourney 回调通知", description = "由 Midjourney Proxy 回调") | 79 | + @Operation(summary = "Midjourney 生成图片的回调通知", description = "由 Midjourney Proxy 回调") |
| 75 | @PostMapping("/midjourney-notify") | 80 | @PostMapping("/midjourney-notify") |
| 76 | @PermitAll | 81 | @PermitAll |
| 77 | - public void midjourneyNotify(@RequestBody MidjourneyNotifyReqVO notifyReqVO) { | ||
| 78 | - imageService.midjourneyNotify(notifyReqVO); | 82 | + public void midjourneyNotify(@RequestBody MidjourneyApi.Notify notify) { |
| 83 | + imageService.midjourneyNotify(notify); | ||
| 79 | } | 84 | } |
| 80 | 85 | ||
| 81 | @Operation(summary = "Midjourney Action", description = "例如说:放大、缩小、U1、U2 等") | 86 | @Operation(summary = "Midjourney Action", description = "例如说:放大、缩小、U1、U2 等") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyImagineReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import jakarta.validation.constraints.NotNull; | ||
| 5 | -import lombok.Data; | ||
| 6 | -import lombok.experimental.Accessors; | ||
| 7 | - | ||
| 8 | -import java.util.List; | ||
| 9 | - | ||
| 10 | -// TODO @fan:待定 | ||
| 11 | -/** | ||
| 12 | - * midjourney req | ||
| 13 | - * | ||
| 14 | - * @author fansili | ||
| 15 | - * @time 2024/4/28 17:42 | ||
| 16 | - * @since 1.0 | ||
| 17 | - */ | ||
| 18 | -@Data | ||
| 19 | -@Accessors(chain = true) | ||
| 20 | -public class AiImageMidjourneyImagineReqVO { | ||
| 21 | - | ||
| 22 | - @Schema(description = "提示词") | ||
| 23 | - @NotNull(message = "提示词不能为空!") | ||
| 24 | - private String prompt; | ||
| 25 | - | ||
| 26 | - @Schema(description = "模型(midjourney、niji)") | ||
| 27 | - private String model; | ||
| 28 | - | ||
| 29 | - @Schema(description = "图片宽度 --ar 设置") | ||
| 30 | - private Integer width; | ||
| 31 | - | ||
| 32 | - @Schema(description = "图片高度 --ar 设置") | ||
| 33 | - private Integer height; | ||
| 34 | - | ||
| 35 | - @Schema(description = "版本号 --v 设置") | ||
| 36 | - private String version; | ||
| 37 | - | ||
| 38 | - @Schema(description = "垫图(参考图)base64数组") | ||
| 39 | - private List<String> base64Array; | ||
| 40 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageMidjourneyOperationsVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | ||
| 2 | - | ||
| 3 | -import lombok.Data; | ||
| 4 | -import lombok.experimental.Accessors; | ||
| 5 | - | ||
| 6 | -/** | ||
| 7 | - * mj 保存 components 记录 | ||
| 8 | - * | ||
| 9 | - * "components": [ | ||
| 10 | - * { | ||
| 11 | - * "custom_id": "MJ::JOB::upsample::1::5d32f4e8-8d2f-4bef-82d8-bf517e3c3660", | ||
| 12 | - * "style": 2, | ||
| 13 | - * "label": "U1", | ||
| 14 | - * "type": 2 | ||
| 15 | - * }, | ||
| 16 | - * ] | ||
| 17 | - * | ||
| 18 | - * @author fansili | ||
| 19 | - * @time 2024/5/8 14:44 | ||
| 20 | - * @since 1.0 | ||
| 21 | - */ | ||
| 22 | -@Data | ||
| 23 | -@Accessors(chain = true) | ||
| 24 | -public class AiImageMidjourneyOperationsVO { | ||
| 25 | - | ||
| 26 | - private String custom_id; | ||
| 27 | - private String style; | ||
| 28 | - private String label; | ||
| 29 | - private String type; | ||
| 30 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/AiImageRespVO.java
| 1 | package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | 1 | package cn.iocoder.yudao.module.ai.controller.admin.image.vo; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 3 | import io.swagger.v3.oas.annotations.media.Schema; | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | import lombok.Data; | 5 | import lombok.Data; |
| 5 | 6 | ||
| @@ -46,14 +47,11 @@ public class AiImageRespVO { | @@ -46,14 +47,11 @@ public class AiImageRespVO { | ||
| 46 | @Schema(description = "绘制参数") | 47 | @Schema(description = "绘制参数") |
| 47 | private Map<String, String> options; | 48 | private Map<String, String> options; |
| 48 | 49 | ||
| 49 | - @Schema(description = "绘画 response") | ||
| 50 | - private MidjourneyNotifyReqVO response; | ||
| 51 | - | ||
| 52 | // TODO @fan:进度是百分比,还是一个数字哈?感觉这个可以统一成通用字段; | 50 | // TODO @fan:进度是百分比,还是一个数字哈?感觉这个可以统一成通用字段; |
| 53 | @Schema(description = "mj 进度") | 51 | @Schema(description = "mj 进度") |
| 54 | private String progress; | 52 | private String progress; |
| 55 | 53 | ||
| 56 | @Schema(description = "mj buttons 按钮") | 54 | @Schema(description = "mj buttons 按钮") |
| 57 | - private List<MidjourneyNotifyReqVO.Button> buttons; | 55 | + private List<MidjourneyApi.Button> buttons; |
| 58 | 56 | ||
| 59 | } | 57 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/MidjourneyImagineReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import jakarta.validation.constraints.NotNull; | ||
| 5 | -import lombok.Data; | ||
| 6 | - | ||
| 7 | -import java.util.List; | ||
| 8 | - | ||
| 9 | -// TODO @fan:待定 | ||
| 10 | -/** | ||
| 11 | - * Midjourney:Imagine 请求 | ||
| 12 | - * | ||
| 13 | - * @author fansili | ||
| 14 | - * @time 2024/5/30 14:02 | ||
| 15 | - * @since 1.0 | ||
| 16 | - */ | ||
| 17 | -@Data | ||
| 18 | -public class MidjourneyImagineReqVO { | ||
| 19 | - | ||
| 20 | - @Schema(description = "垫图(参考图)base64数组", required = false) | ||
| 21 | - private List<String> base64Array; | ||
| 22 | - | ||
| 23 | - @Schema(description = "通知地址", required = false) | ||
| 24 | - @NotNull(message = "回调地址不能为空!") | ||
| 25 | - private String notifyHook; | ||
| 26 | - | ||
| 27 | - @Schema(description = "提示词", required = true) | ||
| 28 | - @NotNull(message = "提示词不能为空!") | ||
| 29 | - private String prompt; | ||
| 30 | - | ||
| 31 | - @Schema(description = "自定义参数", required = false) | ||
| 32 | - private String state; | ||
| 33 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/MidjourneyNotifyReqVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import lombok.Data; | ||
| 5 | - | ||
| 6 | -import java.util.List; | ||
| 7 | - | ||
| 8 | -/** | ||
| 9 | - * Midjourney Proxy 通知回调 | ||
| 10 | - * | ||
| 11 | - * - Midjourney Proxy:通知回调 bean 是 com.github.novicezk.midjourney.support.Task | ||
| 12 | - * - 毫秒 api 通知回调文档地址:https://gpt-best.apifox.cn/doc-3530863 | ||
| 13 | - * | ||
| 14 | - * @author fansili | ||
| 15 | - * @time 2024/5/31 10:37 | ||
| 16 | - * @since 1.0 | ||
| 17 | - */ | ||
| 18 | -@Data | ||
| 19 | -public class MidjourneyNotifyReqVO { | ||
| 20 | - | ||
| 21 | - @Schema(description = "job id") | ||
| 22 | - private String id; | ||
| 23 | - | ||
| 24 | - @Schema(description = "任务类型 MidjourneyTaskActionEnum") | ||
| 25 | - private String action; | ||
| 26 | - @Schema(description = "任务状态 MidjourneyTaskStatusEnum") | ||
| 27 | - private String status; | ||
| 28 | - | ||
| 29 | - @Schema(description = "提示词") | ||
| 30 | - private String prompt; | ||
| 31 | - @Schema(description = "提示词-英文") | ||
| 32 | - private String promptEn; | ||
| 33 | - | ||
| 34 | - @Schema(description = "任务描述") | ||
| 35 | - private String description; | ||
| 36 | - @Schema(description = "自定义参数") | ||
| 37 | - private String state; | ||
| 38 | - | ||
| 39 | - @Schema(description = "提交时间") | ||
| 40 | - private Long submitTime; | ||
| 41 | - @Schema(description = "开始执行时间") | ||
| 42 | - private Long startTime; | ||
| 43 | - @Schema(description = "结束时间") | ||
| 44 | - private Long finishTime; | ||
| 45 | - | ||
| 46 | - @Schema(description = "图片url") | ||
| 47 | - private String imageUrl; | ||
| 48 | - | ||
| 49 | - @Schema(description = "任务进度") | ||
| 50 | - private String progress; | ||
| 51 | - @Schema(description = "失败原因") | ||
| 52 | - private String failReason; | ||
| 53 | - | ||
| 54 | - @Schema(description = "任务完成后的可执行按钮") | ||
| 55 | - private List<Button> buttons; | ||
| 56 | - | ||
| 57 | - @Data | ||
| 58 | - public static class Button { | ||
| 59 | - | ||
| 60 | - @Schema(description = "MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识") | ||
| 61 | - private String customId; | ||
| 62 | - | ||
| 63 | - @Schema(description = "图标 emoji") | ||
| 64 | - private String emoji; | ||
| 65 | - | ||
| 66 | - @Schema(description = "Make Variations 文本") | ||
| 67 | - private String label; | ||
| 68 | - | ||
| 69 | - @Schema(description = "类型,系统内部使用") | ||
| 70 | - private String type; | ||
| 71 | - | ||
| 72 | - @Schema(description = "样式: 2(Primary)、3(Green)") | ||
| 73 | - private String style; | ||
| 74 | - } | ||
| 75 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/MidjourneySubmitRespVO.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.controller.admin.image.vo; | ||
| 2 | - | ||
| 3 | -import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | -import lombok.Data; | ||
| 5 | - | ||
| 6 | -import java.util.Map; | ||
| 7 | - | ||
| 8 | -// TODO @fan:待定 | ||
| 9 | -/** | ||
| 10 | - * Midjourney:Imagine 请求 | ||
| 11 | - * | ||
| 12 | - * @author fansili | ||
| 13 | - * @time 2024/5/30 14:02 | ||
| 14 | - * @since 1.0 | ||
| 15 | - */ | ||
| 16 | -@Data | ||
| 17 | -public class MidjourneySubmitRespVO { | ||
| 18 | - | ||
| 19 | - @Schema(description = "状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)") | ||
| 20 | - private String code; | ||
| 21 | - | ||
| 22 | - @Schema(description = "描述") | ||
| 23 | - private String description; | ||
| 24 | - | ||
| 25 | - @Schema(description = "扩展字段") | ||
| 26 | - private Map<String, Object> properties; | ||
| 27 | - | ||
| 28 | - @Schema(description = "任务ID") | ||
| 29 | - private String result; | ||
| 30 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/image/vo/midjourney/AiImageMidjourneyImagineReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney; | ||
| 2 | + | ||
| 3 | +import io.swagger.v3.oas.annotations.media.Schema; | ||
| 4 | +import jakarta.validation.constraints.NotEmpty; | ||
| 5 | +import jakarta.validation.constraints.NotNull; | ||
| 6 | +import lombok.Data; | ||
| 7 | + | ||
| 8 | +import java.util.List; | ||
| 9 | + | ||
| 10 | +@Schema(description = "管理后台 - 绘画生成(Midjourney) Request VO") | ||
| 11 | +@Data | ||
| 12 | +public class AiImageMidjourneyImagineReqVO { | ||
| 13 | + | ||
| 14 | + @Schema(description = "提示词", requiredMode = Schema.RequiredMode.REQUIRED, example = "中国神龙") | ||
| 15 | + @NotEmpty(message = "提示词不能为空!") | ||
| 16 | + private String prompt; | ||
| 17 | + | ||
| 18 | + @Schema(description = "模型", requiredMode = Schema.RequiredMode.REQUIRED, example = "midjourney") | ||
| 19 | + @NotEmpty(message = "模型不能为空") | ||
| 20 | + private String model; // 参考 MidjourneyApi.ModelEnum | ||
| 21 | + | ||
| 22 | + @Schema(description = "图片宽度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 23 | + @NotNull(message = "图片宽度不能为空") | ||
| 24 | + private Integer width; | ||
| 25 | + | ||
| 26 | + @Schema(description = "图片高度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1") | ||
| 27 | + @NotNull(message = "图片高度不能为空") | ||
| 28 | + private Integer height; | ||
| 29 | + | ||
| 30 | + @Schema(description = "版本号", requiredMode = Schema.RequiredMode.REQUIRED, example = "6.0") | ||
| 31 | + @NotEmpty(message = "版本号不能为空") | ||
| 32 | + private String version; | ||
| 33 | + | ||
| 34 | + // TODO @fan:参考图,建议用 referImageUrl。 | ||
| 35 | + @Schema(description = "垫图(参考图)base64数组") | ||
| 36 | + private List<String> base64Array; | ||
| 37 | + | ||
| 38 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/image/AiImageDO.java
| 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.image; | 1 | package cn.iocoder.yudao.module.ai.dal.dataobject.image; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 3 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 4 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 4 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 5 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.MidjourneyNotifyReqVO; | ||
| 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; | 6 | import cn.iocoder.yudao.module.ai.dal.dataobject.model.AiChatModelDO; |
| 7 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | 7 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; |
| 8 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; | 8 | import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; |
| @@ -28,8 +28,6 @@ import java.util.Map; | @@ -28,8 +28,6 @@ import java.util.Map; | ||
| 28 | @Data | 28 | @Data |
| 29 | public class AiImageDO extends BaseDO { | 29 | public class AiImageDO extends BaseDO { |
| 30 | 30 | ||
| 31 | - // TODO @fan:1)使用 java 注释哈,不要注解。2)关联、枚举字段,要关联到对应类,参考 AiChatMessageDO 的注释 | ||
| 32 | - | ||
| 33 | /** | 31 | /** |
| 34 | * 编号 | 32 | * 编号 |
| 35 | */ | 33 | */ |
| @@ -76,7 +74,7 @@ public class AiImageDO extends BaseDO { | @@ -76,7 +74,7 @@ public class AiImageDO extends BaseDO { | ||
| 76 | * | 74 | * |
| 77 | * 枚举 {@link AiImageStatusEnum} | 75 | * 枚举 {@link AiImageStatusEnum} |
| 78 | */ | 76 | */ |
| 79 | - private String status; | 77 | + private Integer status; |
| 80 | 78 | ||
| 81 | /** | 79 | /** |
| 82 | * 图片地址 | 80 | * 图片地址 |
| @@ -97,22 +95,10 @@ public class AiImageDO extends BaseDO { | @@ -97,22 +95,10 @@ public class AiImageDO extends BaseDO { | ||
| 97 | private Map<String, Object> options; | 95 | private Map<String, Object> options; |
| 98 | 96 | ||
| 99 | /** | 97 | /** |
| 100 | - * 绘画 response | ||
| 101 | - */ | ||
| 102 | - @TableField(typeHandler = JacksonTypeHandler.class) | ||
| 103 | - private MidjourneyNotifyReqVO response; | ||
| 104 | - | ||
| 105 | - // TODO @fan:这个建议 Double | ||
| 106 | - /** | ||
| 107 | - * mj 进度(10%、50%、100%) | ||
| 108 | - */ | ||
| 109 | - private String progress; | ||
| 110 | - | ||
| 111 | - /** | ||
| 112 | * mj buttons 按钮 | 98 | * mj buttons 按钮 |
| 113 | */ | 99 | */ |
| 114 | @TableField(typeHandler = ButtonTypeHandler.class) | 100 | @TableField(typeHandler = ButtonTypeHandler.class) |
| 115 | - private List<MidjourneyNotifyReqVO.Button> buttons; | 101 | + private List<MidjourneyApi.Button> buttons; |
| 116 | 102 | ||
| 117 | /** | 103 | /** |
| 118 | * midjourney proxy 关联的 task id | 104 | * midjourney proxy 关联的 task id |
| @@ -124,12 +110,11 @@ public class AiImageDO extends BaseDO { | @@ -124,12 +110,11 @@ public class AiImageDO extends BaseDO { | ||
| 124 | */ | 110 | */ |
| 125 | private String errorMessage; | 111 | private String errorMessage; |
| 126 | 112 | ||
| 127 | - // TODO @芋艿:看看是不是 MidjourneyNotifyReqVO.Button 搞到 MJ API 那 | ||
| 128 | public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> { | 113 | public static class ButtonTypeHandler extends AbstractJsonTypeHandler<Object> { |
| 129 | 114 | ||
| 130 | @Override | 115 | @Override |
| 131 | protected Object parse(String json) { | 116 | protected Object parse(String json) { |
| 132 | - return JsonUtils.parseArray(json, MidjourneyNotifyReqVO.Button.class); | 117 | + return JsonUtils.parseArray(json, MidjourneyApi.Button.class); |
| 133 | } | 118 | } |
| 134 | 119 | ||
| 135 | @Override | 120 | @Override |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/image/AiImageMapper.java
| 1 | package cn.iocoder.yudao.module.ai.dal.mysql.image; | 1 | package cn.iocoder.yudao.module.ai.dal.mysql.image; |
| 2 | 2 | ||
| 3 | -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | 5 | import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; |
| 7 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; | 6 | import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; |
| 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 7 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 9 | -import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | ||
| 10 | -import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; | ||
| 11 | import org.apache.ibatis.annotations.Mapper; | 8 | import org.apache.ibatis.annotations.Mapper; |
| 12 | 9 | ||
| 13 | import java.util.List; | 10 | import java.util.List; |
| @@ -20,52 +17,19 @@ import java.util.List; | @@ -20,52 +17,19 @@ import java.util.List; | ||
| 20 | @Mapper | 17 | @Mapper |
| 21 | public interface AiImageMapper extends BaseMapperX<AiImageDO> { | 18 | public interface AiImageMapper extends BaseMapperX<AiImageDO> { |
| 22 | 19 | ||
| 23 | - // TODO @fan:这个建议,直接使用 update,service 拼接要改的状态哈 | ||
| 24 | - | ||
| 25 | - /** | ||
| 26 | - * 更新 - 根据 messageId | ||
| 27 | - * | ||
| 28 | - * @param mjNonceId | ||
| 29 | - * @param aiImageDO | ||
| 30 | - */ | ||
| 31 | - default void updateByMjNonce(Long mjNonceId, AiImageDO aiImageDO) { | ||
| 32 | -// this.update(aiImageDO, new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getMjNonceId, mjNonceId)); | ||
| 33 | - return; | ||
| 34 | - } | ||
| 35 | - | ||
| 36 | - /** | ||
| 37 | - * 查询 - 根据 job id | ||
| 38 | - * | ||
| 39 | - * @param id | ||
| 40 | - * @return | ||
| 41 | - */ | ||
| 42 | - default AiImageDO selectByJobId(String id) { | ||
| 43 | - return this.selectOne(new LambdaQueryWrapperX<AiImageDO>().eq(AiImageDO::getTaskId, id)); | 20 | + default AiImageDO selectByTaskId(String taskId) { |
| 21 | + return this.selectOne(AiImageDO::getTaskId, taskId); | ||
| 44 | } | 22 | } |
| 45 | 23 | ||
| 46 | - /** | ||
| 47 | - * 查询 - page | ||
| 48 | - * | ||
| 49 | - * @param userId | ||
| 50 | - * @param pageReqVO | ||
| 51 | - * @return | ||
| 52 | - */ | ||
| 53 | default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) { | 24 | default PageResult<AiImageDO> selectPage(Long userId, PageParam pageReqVO) { |
| 54 | return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>() | 25 | return selectPage(pageReqVO, new LambdaQueryWrapperX<AiImageDO>() |
| 55 | .eq(AiImageDO::getUserId, userId) | 26 | .eq(AiImageDO::getUserId, userId) |
| 56 | .orderByDesc(AiImageDO::getId)); | 27 | .orderByDesc(AiImageDO::getId)); |
| 57 | } | 28 | } |
| 58 | 29 | ||
| 59 | - /** | ||
| 60 | - * 查询 - 根据 status 和 platform | ||
| 61 | - * | ||
| 62 | - * @return | ||
| 63 | - */ | ||
| 64 | - default List<AiImageDO> selectByStatusAndPlatform(AiImageStatusEnum statusEnum, AiPlatformEnum platformEnum) { | ||
| 65 | - return this.selectList(new LambdaUpdateWrapper<AiImageDO>() | ||
| 66 | - .eq(AiImageDO::getStatus, statusEnum.getStatus()) | ||
| 67 | - .eq(AiImageDO::getPlatform, platformEnum.getPlatform()) | ||
| 68 | - ); | 30 | + default List<AiImageDO> selectListByStatusAndPlatform(Integer status, String platform) { |
| 31 | + return selectList(AiImageDO::getStatus, status, | ||
| 32 | + AiImageDO::getPlatform, platform); | ||
| 69 | } | 33 | } |
| 70 | 34 | ||
| 71 | } | 35 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/MidjourneyJob.java deleted
| 1 | -package cn.iocoder.yudao.module.ai.job; | ||
| 2 | - | ||
| 3 | -import cn.hutool.core.collection.CollUtil; | ||
| 4 | -import cn.iocoder.yudao.framework.ai.core.enums.AiPlatformEnum; | ||
| 5 | -import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 6 | -import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; | ||
| 7 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | ||
| 8 | -import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | ||
| 9 | -import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||
| 10 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.MidjourneyNotifyReqVO; | ||
| 11 | -import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | ||
| 12 | -import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; | ||
| 13 | -import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | ||
| 14 | -import cn.iocoder.yudao.module.ai.service.image.AiImageService; | ||
| 15 | -import lombok.extern.slf4j.Slf4j; | ||
| 16 | -import org.springframework.beans.factory.annotation.Autowired; | ||
| 17 | -import org.springframework.stereotype.Component; | ||
| 18 | - | ||
| 19 | -import java.util.ArrayList; | ||
| 20 | -import java.util.List; | ||
| 21 | -import java.util.Map; | ||
| 22 | - | ||
| 23 | -/** | ||
| 24 | - * midjourney job 定时拉去 midjourney 绘制状态 | ||
| 25 | - * | ||
| 26 | - * @author fansili | ||
| 27 | - * @time 2024/6/5 14:55 | ||
| 28 | - * @since 1.0 | ||
| 29 | - */ | ||
| 30 | -@Component | ||
| 31 | -@Slf4j | ||
| 32 | -public class MidjourneyJob implements JobHandler { | ||
| 33 | - | ||
| 34 | - // TODO @fan:@Resource | ||
| 35 | - @Autowired(required = false) | ||
| 36 | - private MidjourneyApi midjourneyApi; | ||
| 37 | - @Autowired | ||
| 38 | - private AiImageMapper imageMapper; | ||
| 39 | - @Autowired | ||
| 40 | - private AiImageService imageService; | ||
| 41 | - | ||
| 42 | - // TODO @fan:这个方法,建议实现到 AiImageService,例如说 midjourneySync,返回 int 同步数量。 | ||
| 43 | - @Override | ||
| 44 | - public String execute(String param) { | ||
| 45 | - // 1、获取 midjourney 平台,状态在 “进行中” 的 image | ||
| 46 | - List<AiImageDO> imageList = imageMapper.selectByStatusAndPlatform(AiImageStatusEnum.IN_PROGRESS, AiPlatformEnum.MIDJOURNEY); | ||
| 47 | - log.info("Midjourney 同步 - 任务数量 {}!", imageList.size()); | ||
| 48 | - if (CollUtil.isEmpty(imageList)) { | ||
| 49 | - return "Midjourney 同步 - 数量为空!"; | ||
| 50 | - } | ||
| 51 | - log.info("Midjourney 同步 - 开始..."); | ||
| 52 | - // 2、批量拉去 task 信息 | ||
| 53 | - List<MidjourneyApi.NotifyRequest> taskList = midjourneyApi | ||
| 54 | - .listByCondition(CollectionUtils.convertSet(imageList, AiImageDO::getTaskId)); | ||
| 55 | - Map<String, MidjourneyApi.NotifyRequest> taskIdMap | ||
| 56 | - = CollectionUtils.convertMap(taskList, MidjourneyApi.NotifyRequest::id); | ||
| 57 | - // 3、更新 image 状态 | ||
| 58 | - List<AiImageDO> updateImageList = new ArrayList<>(); | ||
| 59 | - for (AiImageDO aiImageDO : imageList) { | ||
| 60 | - // 3.1 排除掉空的情况 | ||
| 61 | - if (!taskIdMap.containsKey(aiImageDO.getTaskId())) { | ||
| 62 | - log.warn("Midjourney 同步 - {} 任务为空!", aiImageDO.getTaskId()); | ||
| 63 | - continue; | ||
| 64 | - } | ||
| 65 | - // TODO @ 3.1 和 3.2 是不是融合下;get,然后判空,continue; | ||
| 66 | - // 3.2 获取通知对象 | ||
| 67 | - MidjourneyApi.NotifyRequest notifyRequest = taskIdMap.get(aiImageDO.getTaskId()); | ||
| 68 | - // 3.2 构建更新对象 | ||
| 69 | - // TODO @fan:建议 List<MidjourneyNotifyReqVO> 作为 imageService 去更新; | ||
| 70 | - // TODO @芋艿 BeanUtils.toBean 转换为 null | ||
| 71 | - updateImageList.add(imageService.buildUpdateImage(aiImageDO.getId(), | ||
| 72 | - JsonUtils.parseObject(JsonUtils.toJsonString(notifyRequest), MidjourneyNotifyReqVO.class))); | ||
| 73 | - } | ||
| 74 | - // 4、批了更新 updateImageList | ||
| 75 | - imageMapper.updateBatch(updateImageList); | ||
| 76 | - return "Midjourney 同步 - ".concat(String.valueOf(updateImageList.size())).concat(" 任务!"); | ||
| 77 | - } | ||
| 78 | - | ||
| 79 | -} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/job/image/MidjourneySyncJob.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.ai.job.image; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.quartz.core.handler.JobHandler; | ||
| 4 | +import cn.iocoder.yudao.module.ai.service.image.AiImageService; | ||
| 5 | +import jakarta.annotation.Resource; | ||
| 6 | +import lombok.extern.slf4j.Slf4j; | ||
| 7 | +import org.springframework.stereotype.Component; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * Midjourney 同步 Job:定时拉去 midjourney 绘制状态 | ||
| 11 | + * | ||
| 12 | + * @author fansili | ||
| 13 | + */ | ||
| 14 | +@Component | ||
| 15 | +@Slf4j | ||
| 16 | +public class MidjourneySyncJob implements JobHandler { | ||
| 17 | + | ||
| 18 | + @Resource | ||
| 19 | + private AiImageService imageService; | ||
| 20 | + | ||
| 21 | + @Override | ||
| 22 | + public String execute(String param) { | ||
| 23 | + Integer count = imageService.midjourneySync(); | ||
| 24 | + log.info("[execute][同步 Midjourney ({}) 个]", count); | ||
| 25 | + return String.format("同步 Midjourney %s 个", count); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | +} |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageService.java
| 1 | package cn.iocoder.yudao.module.ai.service.image; | 1 | package cn.iocoder.yudao.module.ai.service.image; |
| 2 | 2 | ||
| 3 | +import cn.iocoder.yudao.framework.ai.core.model.midjourney.api.MidjourneyApi; | ||
| 3 | import cn.iocoder.yudao.framework.common.pojo.PageParam; | 4 | import cn.iocoder.yudao.framework.common.pojo.PageParam; |
| 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.MidjourneyNotifyReqVO; | ||
| 6 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; | 6 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; |
| 7 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; | 7 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiImageMidjourneyImagineReqVO; |
| 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 8 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 9 | 9 | ||
| 10 | /** | 10 | /** |
| @@ -41,38 +41,37 @@ public interface AiImageService { | @@ -41,38 +41,37 @@ public interface AiImageService { | ||
| 41 | Long drawImage(Long userId, AiImageDrawReqVO drawReqVO); | 41 | Long drawImage(Long userId, AiImageDrawReqVO drawReqVO); |
| 42 | 42 | ||
| 43 | /** | 43 | /** |
| 44 | - * Midjourney imagine(绘画) | 44 | + * 删除【我的】绘画记录 |
| 45 | * | 45 | * |
| 46 | + * @param id 绘画编号 | ||
| 46 | * @param userId 用户编号 | 47 | * @param userId 用户编号 |
| 47 | - * @param imagineReqVO 绘制请求 | ||
| 48 | - * @return 绘画编号 | ||
| 49 | */ | 48 | */ |
| 50 | - Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO imagineReqVO); | 49 | + void deleteImageMy(Long id, Long userId); |
| 50 | + | ||
| 51 | + // ================ midjourney 专属 ================ | ||
| 51 | 52 | ||
| 52 | /** | 53 | /** |
| 53 | - * 删除【我的】绘画记录 | 54 | + * 【Midjourney】生成图片 |
| 54 | * | 55 | * |
| 55 | - * @param id 绘画编号 | ||
| 56 | * @param userId 用户编号 | 56 | * @param userId 用户编号 |
| 57 | + * @param reqVO 绘制请求 | ||
| 58 | + * @return 绘画编号 | ||
| 57 | */ | 59 | */ |
| 58 | - void deleteImageMy(Long id, Long userId); | 60 | + Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO reqVO); |
| 59 | 61 | ||
| 60 | /** | 62 | /** |
| 61 | - * midjourney proxy - 回调通知 | 63 | + * 【Midjourney】同步图片进展 |
| 62 | * | 64 | * |
| 63 | - * @param notifyReqVO | ||
| 64 | - * @return | 65 | + * @return 同步成功数量 |
| 65 | */ | 66 | */ |
| 66 | - void midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO); | 67 | + Integer midjourneySync(); |
| 67 | 68 | ||
| 68 | /** | 69 | /** |
| 69 | - * 构建 midjourney - 更新对象 | 70 | + * 【Midjourney】通知图片进展 |
| 70 | * | 71 | * |
| 71 | - * @param imageId | ||
| 72 | - * @param notifyReqVO | ||
| 73 | - * @return | 72 | + * @param notify 通知 |
| 74 | */ | 73 | */ |
| 75 | - AiImageDO buildUpdateImage(Long imageId, MidjourneyNotifyReqVO notifyReqVO); | 74 | + void midjourneyNotify(MidjourneyApi.Notify notify); |
| 76 | 75 | ||
| 77 | /** | 76 | /** |
| 78 | * midjourney - action(放大、缩小、U1、U2...) | 77 | * midjourney - action(放大、缩小、U1、U2...) |
| @@ -83,4 +82,5 @@ public interface AiImageService { | @@ -83,4 +82,5 @@ public interface AiImageService { | ||
| 83 | * @return | 82 | * @return |
| 84 | */ | 83 | */ |
| 85 | void midjourneyAction(Long loginUserId, Long imageId, String customId); | 84 | void midjourneyAction(Long loginUserId, Long imageId, String customId); |
| 85 | + | ||
| 86 | } | 86 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
| @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.service.image; | @@ -2,7 +2,7 @@ package cn.iocoder.yudao.module.ai.service.image; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.bean.BeanUtil; | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | import cn.hutool.core.codec.Base64; | 4 | import cn.hutool.core.codec.Base64; |
| 5 | -import cn.hutool.core.exceptions.ExceptionUtil; | 5 | +import cn.hutool.core.collection.CollUtil; |
| 6 | import cn.hutool.core.map.MapUtil; | 6 | import cn.hutool.core.map.MapUtil; |
| 7 | import cn.hutool.core.util.ObjUtil; | 7 | import cn.hutool.core.util.ObjUtil; |
| 8 | import cn.hutool.core.util.StrUtil; | 8 | import cn.hutool.core.util.StrUtil; |
| @@ -14,8 +14,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; | @@ -14,8 +14,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageParam; | ||
| 14 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 14 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 15 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 15 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 16 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; | 16 | import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageDrawReqVO; |
| 17 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.AiImageMidjourneyImagineReqVO; | ||
| 18 | -import cn.iocoder.yudao.module.ai.controller.admin.image.vo.MidjourneyNotifyReqVO; | 17 | +import cn.iocoder.yudao.module.ai.controller.admin.image.vo.midjourney.AiImageMidjourneyImagineReqVO; |
| 19 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; | 18 | import cn.iocoder.yudao.module.ai.dal.dataobject.image.AiImageDO; |
| 20 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; | 19 | import cn.iocoder.yudao.module.ai.dal.mysql.image.AiImageMapper; |
| 21 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; | 20 | import cn.iocoder.yudao.module.ai.enums.image.AiImageStatusEnum; |
| @@ -29,15 +28,17 @@ import org.springframework.ai.image.ImagePrompt; | @@ -29,15 +28,17 @@ import org.springframework.ai.image.ImagePrompt; | ||
| 29 | import org.springframework.ai.image.ImageResponse; | 28 | import org.springframework.ai.image.ImageResponse; |
| 30 | import org.springframework.ai.openai.OpenAiImageOptions; | 29 | import org.springframework.ai.openai.OpenAiImageOptions; |
| 31 | import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; | 30 | import org.springframework.ai.stabilityai.api.StabilityAiImageOptions; |
| 32 | -import org.springframework.beans.factory.annotation.Autowired; | ||
| 33 | import org.springframework.beans.factory.annotation.Value; | 31 | import org.springframework.beans.factory.annotation.Value; |
| 34 | import org.springframework.scheduling.annotation.Async; | 32 | import org.springframework.scheduling.annotation.Async; |
| 35 | import org.springframework.stereotype.Service; | 33 | import org.springframework.stereotype.Service; |
| 36 | import org.springframework.transaction.annotation.Transactional; | 34 | import org.springframework.transaction.annotation.Transactional; |
| 37 | 35 | ||
| 38 | import java.util.List; | 36 | import java.util.List; |
| 37 | +import java.util.Map; | ||
| 39 | 38 | ||
| 40 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 39 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 40 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap; | ||
| 41 | +import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; | ||
| 41 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; | 42 | import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.*; |
| 42 | 43 | ||
| 43 | /** | 44 | /** |
| @@ -51,12 +52,16 @@ public class AiImageServiceImpl implements AiImageService { | @@ -51,12 +52,16 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 51 | 52 | ||
| 52 | @Resource | 53 | @Resource |
| 53 | private AiImageMapper imageMapper; | 54 | private AiImageMapper imageMapper; |
| 55 | + | ||
| 54 | @Resource | 56 | @Resource |
| 55 | private FileApi fileApi; | 57 | private FileApi fileApi; |
| 58 | + | ||
| 56 | @Resource | 59 | @Resource |
| 57 | private AiApiKeyService apiKeyService; | 60 | private AiApiKeyService apiKeyService; |
| 58 | - @Autowired(required = false) | 61 | + |
| 62 | + @Resource | ||
| 59 | private MidjourneyApi midjourneyApi; | 63 | private MidjourneyApi midjourneyApi; |
| 64 | + | ||
| 60 | @Value("${ai.midjourney-proxy.notifyUrl:http://127.0.0.1:48080/admin-api/ai/image/midjourney-notify}") | 65 | @Value("${ai.midjourney-proxy.notifyUrl:http://127.0.0.1:48080/admin-api/ai/image/midjourney-notify}") |
| 61 | private String midjourneyNotifyUrl; | 66 | private String midjourneyNotifyUrl; |
| 62 | 67 | ||
| @@ -74,7 +79,7 @@ public class AiImageServiceImpl implements AiImageService { | @@ -74,7 +79,7 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 74 | public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { | 79 | public Long drawImage(Long userId, AiImageDrawReqVO drawReqVO) { |
| 75 | // 1. 保存数据库 | 80 | // 1. 保存数据库 |
| 76 | AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) | 81 | AiImageDO image = BeanUtils.toBean(drawReqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) |
| 77 | - .setWidth(drawReqVO.getWidth()).setHeight(drawReqVO.getHeight()).setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | 82 | + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); |
| 78 | imageMapper.insert(image); | 83 | imageMapper.insert(image); |
| 79 | // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果 | 84 | // 2. 异步绘制,后续前端通过返回的 id 进行轮询结果 |
| 80 | getSelf().executeDrawImage(image, drawReqVO); | 85 | getSelf().executeDrawImage(image, drawReqVO); |
| @@ -122,101 +127,121 @@ public class AiImageServiceImpl implements AiImageService { | @@ -122,101 +127,121 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 122 | } | 127 | } |
| 123 | 128 | ||
| 124 | @Override | 129 | @Override |
| 125 | - @Transactional(rollbackFor = Exception.class) | ||
| 126 | - public Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO req) { | ||
| 127 | - // 1、构建 AiImageDO 并 保存 | ||
| 128 | - AiImageDO image = new AiImageDO() | ||
| 129 | - .setUserId(userId) | ||
| 130 | - .setPrompt(req.getPrompt()) | ||
| 131 | - .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()) | ||
| 132 | - .setModel(req.getModel()) | ||
| 133 | - .setWidth(req.getWidth()) | ||
| 134 | - .setHeight(req.getHeight()) | ||
| 135 | - .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | ||
| 136 | - imageMapper.insert(image); | 130 | + public void deleteImageMy(Long id, Long userId) { |
| 131 | + // 1. 校验是否存在 | ||
| 132 | + AiImageDO image = validateImageExists(id); | ||
| 133 | + if (ObjUtil.notEqual(image.getUserId(), userId)) { | ||
| 134 | + throw exception(AI_IMAGE_NOT_EXISTS); | ||
| 135 | + } | ||
| 136 | + // 2. 删除记录 | ||
| 137 | + imageMapper.deleteById(id); | ||
| 138 | + } | ||
| 137 | 139 | ||
| 138 | - // 3、调用 MidjourneyProxy 提交任务 | 140 | + private AiImageDO validateImageExists(Long id) { |
| 141 | + AiImageDO image = imageMapper.selectById(id); | ||
| 142 | + if (image == null) { | ||
| 143 | + throw exception(AI_IMAGE_NOT_EXISTS); | ||
| 144 | + } | ||
| 145 | + return image; | ||
| 146 | + } | ||
| 139 | 147 | ||
| 140 | - // 3.1、设置 midjourney 扩展参数 | ||
| 141 | - MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest(null, midjourneyNotifyUrl, req.getPrompt(), | ||
| 142 | - buildParams(req.getWidth(), req.getHeight(), req.getVersion(), | ||
| 143 | - MidjourneyApi.ModelEnum.valueOfModel(req.getModel()))); | ||
| 144 | - // 3.2、提交绘画请求 | ||
| 145 | - // TODO @fan:5 这里,失败的情况,到底抛出异常,还是 RespVO,可以参考 OpenAI 的 API 封装 | ||
| 146 | - MidjourneyApi.SubmitResponse submitResponse = midjourneyApi.imagine(imagineRequest); | 148 | + // ================ midjourney 专属 ================ |
| 147 | 149 | ||
| 148 | - // 4、保存任务 id (状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误)) | ||
| 149 | - if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(submitResponse.code())) { | ||
| 150 | - if (submitResponse.description().contains("quota_not_enough")) { | ||
| 151 | - throw exception(AI_IMAGE_SYSTEM_ACCOUNT_INSUFFICIENT_BALANCE, submitResponse.description()); | ||
| 152 | - } | ||
| 153 | - throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, submitResponse.description()); | 150 | + @Override |
| 151 | + @Transactional(rollbackFor = Exception.class) | ||
| 152 | + public Long midjourneyImagine(Long userId, AiImageMidjourneyImagineReqVO reqVO) { | ||
| 153 | + // 1. 保存数据库 | ||
| 154 | + AiImageDO image = BeanUtils.toBean(reqVO, AiImageDO.class).setUserId(userId).setPublicStatus(false) | ||
| 155 | + .setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()) | ||
| 156 | + .setPlatform(AiPlatformEnum.MIDJOURNEY.getPlatform()); | ||
| 157 | + imageMapper.insert(image); | ||
| 158 | + | ||
| 159 | + // 2. 调用 Midjourney Proxy 提交任务 | ||
| 160 | + MidjourneyApi.ImagineRequest imagineRequest = new MidjourneyApi.ImagineRequest( | ||
| 161 | + null, midjourneyNotifyUrl, reqVO.getPrompt(), | ||
| 162 | + MidjourneyApi.ImagineRequest.buildState(reqVO.getWidth(), reqVO.getHeight(), reqVO.getVersion(), reqVO.getModel())); | ||
| 163 | + MidjourneyApi.SubmitResponse imagineResponse = midjourneyApi.imagine(imagineRequest); | ||
| 164 | + | ||
| 165 | + // 3. 情况一【失败】:抛出业务异常 | ||
| 166 | + if (!MidjourneyApi.SubmitCodeEnum.SUCCESS_CODES.contains(imagineResponse.code())) { | ||
| 167 | + String description = imagineResponse.description().contains("quota_not_enough") ? | ||
| 168 | + "账户余额不足" : imagineResponse.description(); | ||
| 169 | + throw exception(AI_IMAGE_MIDJOURNEY_SUBMIT_FAIL, description); | ||
| 154 | } | 170 | } |
| 155 | - // 4.1、更新 taskId 和参数 | 171 | + |
| 172 | + // 4. 情况二【成功】:更新 taskId 和参数 | ||
| 156 | imageMapper.updateById(new AiImageDO() | 173 | imageMapper.updateById(new AiImageDO() |
| 157 | .setId(image.getId()) | 174 | .setId(image.getId()) |
| 158 | - .setTaskId(submitResponse.result()) | ||
| 159 | - .setOptions(BeanUtil.beanToMap(req)) | 175 | + .setTaskId(imagineResponse.result()) |
| 176 | + .setOptions(BeanUtil.beanToMap(reqVO)) | ||
| 160 | ); | 177 | ); |
| 161 | return image.getId(); | 178 | return image.getId(); |
| 162 | } | 179 | } |
| 163 | 180 | ||
| 164 | - | ||
| 165 | @Override | 181 | @Override |
| 166 | - public void deleteImageMy(Long id, Long userId) { | ||
| 167 | - // 1. 校验是否存在 | ||
| 168 | - AiImageDO image = validateImageExists(id); | ||
| 169 | - if (ObjUtil.notEqual(image.getUserId(), userId)) { | ||
| 170 | - throw exception(AI_IMAGE_NOT_EXISTS); | 182 | + public Integer midjourneySync() { |
| 183 | + // 1.1 获取 Midjourney 平台,状态在 “进行中” 的 image | ||
| 184 | + List<AiImageDO> imageList = imageMapper.selectListByStatusAndPlatform( | ||
| 185 | + AiImageStatusEnum.IN_PROGRESS.getStatus(), AiPlatformEnum.MIDJOURNEY.getPlatform()); | ||
| 186 | + if (CollUtil.isEmpty(imageList)) { | ||
| 187 | + return 0; | ||
| 171 | } | 188 | } |
| 172 | - // 2. 删除记录 | ||
| 173 | - imageMapper.deleteById(id); | 189 | + // 1.2 调用 Midjourney Proxy 获取任务进展 |
| 190 | + List<MidjourneyApi.Notify> taskList = midjourneyApi.getTaskList(convertSet(imageList, AiImageDO::getTaskId)); | ||
| 191 | + Map<String, MidjourneyApi.Notify> taskMap = convertMap(taskList, MidjourneyApi.Notify::id); | ||
| 192 | + | ||
| 193 | + // 2. 逐个处理,更新进展 | ||
| 194 | + int count = 0; | ||
| 195 | + for (AiImageDO image : imageList) { | ||
| 196 | + MidjourneyApi.Notify notify = taskMap.get(image.getTaskId()); | ||
| 197 | + if (notify == null) { | ||
| 198 | + log.error("[midjourneySync][image({}) 查询不到进展]", image); | ||
| 199 | + continue; | ||
| 200 | + } | ||
| 201 | + count++; | ||
| 202 | + updateMidjourneyStatus(image, notify); | ||
| 203 | + } | ||
| 204 | + return count; | ||
| 174 | } | 205 | } |
| 175 | 206 | ||
| 176 | @Override | 207 | @Override |
| 177 | - public void midjourneyNotify(MidjourneyNotifyReqVO notifyReqVO) { | ||
| 178 | - // 1、根据 job id 查询关联的 image | ||
| 179 | - AiImageDO image = imageMapper.selectByJobId(notifyReqVO.getId()); | 208 | + public void midjourneyNotify(MidjourneyApi.Notify notify) { |
| 209 | + // 1. 校验 image 存在 | ||
| 210 | + AiImageDO image = imageMapper.selectByTaskId(notify.id()); | ||
| 180 | if (image == null) { | 211 | if (image == null) { |
| 181 | - log.warn("midjourneyNotify 回调的 jobId 不存在! jobId: {}", notifyReqVO.getId()); | 212 | + log.warn("[midjourneyNotify][回调任务({}) 不存在]", notify.id()); |
| 213 | + return; | ||
| 182 | } | 214 | } |
| 183 | - // 2、转换状态 | ||
| 184 | - AiImageDO updateImage = buildUpdateImage(image.getId(), notifyReqVO); | ||
| 185 | - // 3、更新 image 状态 | ||
| 186 | - imageMapper.updateById(updateImage); | 215 | + // 2. 更新状态 |
| 216 | + updateMidjourneyStatus(image, notify); | ||
| 187 | } | 217 | } |
| 188 | 218 | ||
| 189 | - public AiImageDO buildUpdateImage(Long imageId, MidjourneyNotifyReqVO notifyReqVO) { | ||
| 190 | - // 1、转换状态 | ||
| 191 | - String imageStatus = null; | ||
| 192 | - if (StrUtil.isNotBlank(notifyReqVO.getStatus())) { | ||
| 193 | - MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notifyReqVO.getStatus()); | 219 | + private void updateMidjourneyStatus(AiImageDO image, MidjourneyApi.Notify notify) { |
| 220 | + // 1. 转换状态 | ||
| 221 | + Integer status = null; | ||
| 222 | + if (StrUtil.isNotBlank(notify.status())) { | ||
| 223 | + MidjourneyApi.TaskStatusEnum taskStatusEnum = MidjourneyApi.TaskStatusEnum.valueOf(notify.status()); | ||
| 194 | if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) { | 224 | if (MidjourneyApi.TaskStatusEnum.SUCCESS == taskStatusEnum) { |
| 195 | - imageStatus = AiImageStatusEnum.SUCCESS.getStatus(); | 225 | + status = AiImageStatusEnum.SUCCESS.getStatus(); |
| 196 | } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) { | 226 | } else if (MidjourneyApi.TaskStatusEnum.FAILURE == taskStatusEnum) { |
| 197 | - imageStatus = AiImageStatusEnum.FAIL.getStatus(); | 227 | + status = AiImageStatusEnum.FAIL.getStatus(); |
| 198 | } | 228 | } |
| 199 | } | 229 | } |
| 200 | 230 | ||
| 201 | - // 2、上传图片 | ||
| 202 | - String filePath = null; | ||
| 203 | - if (!StrUtil.isBlank(notifyReqVO.getImageUrl())) { | 231 | + // 2. 上传图片 |
| 232 | + String picUrl = null; | ||
| 233 | + if (StrUtil.isNotBlank(notify.imageUrl())) { | ||
| 204 | try { | 234 | try { |
| 205 | - filePath = fileApi.createFile(HttpUtil.downloadBytes(notifyReqVO.getImageUrl())); | 235 | + picUrl = fileApi.createFile(HttpUtil.downloadBytes(notify.imageUrl())); |
| 206 | } catch (Exception e) { | 236 | } catch (Exception e) { |
| 207 | - log.warn("midjourneyNotify 图片上传失败! {} 异常:{}", notifyReqVO.getImageUrl(), ExceptionUtil.getMessage(e)); | 237 | + picUrl = notify.imageUrl(); |
| 238 | + log.warn("[updateMidjourneyStatus][图片({}) 地址({}) 上传失败]", image.getId(), notify.imageUrl(), e); | ||
| 208 | } | 239 | } |
| 209 | } | 240 | } |
| 210 | 241 | ||
| 211 | - // 3、更新 image 状态 | ||
| 212 | - return new AiImageDO() | ||
| 213 | - .setId(imageId) | ||
| 214 | - .setStatus(imageStatus) | ||
| 215 | - .setPicUrl(filePath) | ||
| 216 | - .setProgress(notifyReqVO.getProgress()) | ||
| 217 | - .setResponse(notifyReqVO) | ||
| 218 | - .setButtons(notifyReqVO.getButtons()) | ||
| 219 | - .setErrorMessage(notifyReqVO.getFailReason()); | 242 | + // 3. 更新 image 状态 |
| 243 | + imageMapper.updateById(new AiImageDO().setId(image.getId()).setStatus(status) | ||
| 244 | + .setPicUrl(picUrl).setButtons(notify.buttons()).setErrorMessage(notify.failReason())); | ||
| 220 | } | 245 | } |
| 221 | 246 | ||
| 222 | @Override | 247 | @Override |
| @@ -236,7 +261,6 @@ public class AiImageServiceImpl implements AiImageService { | @@ -236,7 +261,6 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 236 | 261 | ||
| 237 | // 5、新增 image 记录(根据 image 新增一个) | 262 | // 5、新增 image 记录(根据 image 新增一个) |
| 238 | AiImageDO newImage = new AiImageDO(); | 263 | AiImageDO newImage = new AiImageDO(); |
| 239 | - newImage.setId(null); | ||
| 240 | newImage.setUserId(image.getUserId()); | 264 | newImage.setUserId(image.getUserId()); |
| 241 | newImage.setPrompt(image.getPrompt()); | 265 | newImage.setPrompt(image.getPrompt()); |
| 242 | 266 | ||
| @@ -248,20 +272,15 @@ public class AiImageServiceImpl implements AiImageService { | @@ -248,20 +272,15 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 248 | newImage.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); | 272 | newImage.setStatus(AiImageStatusEnum.IN_PROGRESS.getStatus()); |
| 249 | newImage.setPublicStatus(image.getPublicStatus()); | 273 | newImage.setPublicStatus(image.getPublicStatus()); |
| 250 | 274 | ||
| 251 | - newImage.setPicUrl(null); | ||
| 252 | - newImage.setProgress(null); | ||
| 253 | - newImage.setButtons(null); | ||
| 254 | newImage.setOptions(image.getOptions()); | 275 | newImage.setOptions(image.getOptions()); |
| 255 | - newImage.setResponse(image.getResponse()); | ||
| 256 | newImage.setTaskId(submitResponse.result()); | 276 | newImage.setTaskId(submitResponse.result()); |
| 257 | - newImage.setErrorMessage(null); | ||
| 258 | imageMapper.insert(newImage); | 277 | imageMapper.insert(newImage); |
| 259 | } | 278 | } |
| 260 | 279 | ||
| 261 | - private static void validateCustomId(String customId, List<MidjourneyNotifyReqVO.Button> buttons) { | 280 | + private static void validateCustomId(String customId, List<MidjourneyApi.Button> buttons) { |
| 262 | boolean isTrue = false; | 281 | boolean isTrue = false; |
| 263 | - for (MidjourneyNotifyReqVO.Button button : buttons) { | ||
| 264 | - if (button.getCustomId().equals(customId)) { | 282 | + for (MidjourneyApi.Button button : buttons) { |
| 283 | + if (button.customId().equals(customId)) { | ||
| 265 | isTrue = true; | 284 | isTrue = true; |
| 266 | break; | 285 | break; |
| 267 | } | 286 | } |
| @@ -271,14 +290,6 @@ public class AiImageServiceImpl implements AiImageService { | @@ -271,14 +290,6 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 271 | } | 290 | } |
| 272 | } | 291 | } |
| 273 | 292 | ||
| 274 | - private AiImageDO validateImageExists(Long id) { | ||
| 275 | - AiImageDO image = imageMapper.selectById(id); | ||
| 276 | - if (image == null) { | ||
| 277 | - throw exception(AI_IMAGE_NOT_EXISTS); | ||
| 278 | - } | ||
| 279 | - return image; | ||
| 280 | - } | ||
| 281 | - | ||
| 282 | /** | 293 | /** |
| 283 | * 获得自身的代理对象,解决 AOP 生效问题 | 294 | * 获得自身的代理对象,解决 AOP 生效问题 |
| 284 | * | 295 | * |
| @@ -288,28 +299,4 @@ public class AiImageServiceImpl implements AiImageService { | @@ -288,28 +299,4 @@ public class AiImageServiceImpl implements AiImageService { | ||
| 288 | return SpringUtil.getBean(getClass()); | 299 | return SpringUtil.getBean(getClass()); |
| 289 | } | 300 | } |
| 290 | 301 | ||
| 291 | - // TODO @fan:这个是不是应该放在 MJ API 的封装里面搞哈? | ||
| 292 | - | ||
| 293 | - /** | ||
| 294 | - * 构建 Midjourney 自定义参数 | ||
| 295 | - * | ||
| 296 | - * @param width | ||
| 297 | - * @param height | ||
| 298 | - * @param version | ||
| 299 | - * @param model | ||
| 300 | - * @return | ||
| 301 | - */ | ||
| 302 | - private String buildParams(Integer width, Integer height, String version, MidjourneyApi.ModelEnum model) { | ||
| 303 | - StringBuilder params = new StringBuilder(); | ||
| 304 | - // --ar 来设置尺寸 | ||
| 305 | - params.append(String.format(" --ar %s:%s ", width, height)); | ||
| 306 | - // --niji 模型 | ||
| 307 | - if (MidjourneyApi.ModelEnum.NIJI == model) { | ||
| 308 | - params.append(String.format(" --niji %s ", version)); | ||
| 309 | - } else { | ||
| 310 | - // --v 版本 | ||
| 311 | - params.append(String.format(" --v %s ", version)); | ||
| 312 | - } | ||
| 313 | - return params.toString(); | ||
| 314 | - } | ||
| 315 | } | 302 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/pom.xml
| @@ -50,34 +50,6 @@ | @@ -50,34 +50,6 @@ | ||
| 50 | <artifactId>junit</artifactId> | 50 | <artifactId>junit</artifactId> |
| 51 | <scope>test</scope> | 51 | <scope>test</scope> |
| 52 | </dependency> | 52 | </dependency> |
| 53 | - | ||
| 54 | - <!-- TODO fan:这里包,要进一步减少 --> | ||
| 55 | - | ||
| 56 | - <dependency> | ||
| 57 | - <groupId>org.springframework.boot</groupId> | ||
| 58 | - <artifactId>spring-boot-starter-websocket</artifactId> | ||
| 59 | - </dependency> | ||
| 60 | - | ||
| 61 | - <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> | ||
| 62 | - <dependency> | ||
| 63 | - <groupId>com.squareup.okhttp3</groupId> | ||
| 64 | - <artifactId>okhttp</artifactId> | ||
| 65 | - </dependency> | ||
| 66 | - <dependency> | ||
| 67 | - <groupId>io.projectreactor.netty</groupId> | ||
| 68 | - <artifactId>reactor-netty</artifactId> | ||
| 69 | - </dependency> | ||
| 70 | - <dependency> | ||
| 71 | - <groupId>net.dv8tion</groupId> | ||
| 72 | - <artifactId>JDA</artifactId> | ||
| 73 | - <version>5.0.0-beta.21</version> | ||
| 74 | -<!-- <exclusions>--> | ||
| 75 | -<!-- <exclusion>--> | ||
| 76 | -<!-- <groupId>club.minnced</groupId>--> | ||
| 77 | -<!-- <artifactId>opus-java</artifactId>--> | ||
| 78 | -<!-- </exclusion>--> | ||
| 79 | -<!-- </exclusions>--> | ||
| 80 | - </dependency> | ||
| 81 | </dependencies> | 53 | </dependencies> |
| 82 | 54 | ||
| 83 | </project> | 55 | </project> |
| 84 | \ No newline at end of file | 56 | \ No newline at end of file |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/midjourney/api/MidjourneyApi.java
| @@ -16,66 +16,54 @@ import java.util.List; | @@ -16,66 +16,54 @@ import java.util.List; | ||
| 16 | import java.util.Map; | 16 | import java.util.Map; |
| 17 | 17 | ||
| 18 | /** | 18 | /** |
| 19 | - * Midjourney api | 19 | + * Midjourney API |
| 20 | * | 20 | * |
| 21 | * @author fansili | 21 | * @author fansili |
| 22 | - * @time 2024/6/11 15:46 | ||
| 23 | * @since 1.0 | 22 | * @since 1.0 |
| 24 | */ | 23 | */ |
| 25 | @Slf4j | 24 | @Slf4j |
| 26 | public class MidjourneyApi { | 25 | public class MidjourneyApi { |
| 27 | 26 | ||
| 28 | - private static final String URI_IMAGINE = "/submit/imagine"; | ||
| 29 | - private static final String URI_ACTON = "/submit/action"; | ||
| 30 | - private static final String URI_LIST_BY_CONDITION = "/task/list-by-condition"; | ||
| 31 | private final WebClient webClient; | 27 | private final WebClient webClient; |
| 32 | - private final MidjourneyConfig midjourneyConfig; | ||
| 33 | 28 | ||
| 34 | public MidjourneyApi(MidjourneyConfig midjourneyConfig) { | 29 | public MidjourneyApi(MidjourneyConfig midjourneyConfig) { |
| 35 | - this.midjourneyConfig = midjourneyConfig; | ||
| 36 | this.webClient = WebClient.builder() | 30 | this.webClient = WebClient.builder() |
| 37 | .baseUrl(midjourneyConfig.getUrl()) | 31 | .baseUrl(midjourneyConfig.getUrl()) |
| 38 | .defaultHeaders(ApiUtils.getJsonContentHeaders(midjourneyConfig.getKey())) | 32 | .defaultHeaders(ApiUtils.getJsonContentHeaders(midjourneyConfig.getKey())) |
| 39 | .build(); | 33 | .build(); |
| 40 | } | 34 | } |
| 41 | 35 | ||
| 42 | - | ||
| 43 | /** | 36 | /** |
| 44 | * imagine - 根据提示词提交绘画任务 | 37 | * imagine - 根据提示词提交绘画任务 |
| 45 | * | 38 | * |
| 46 | - * @param imagineReqVO | ||
| 47 | - * @return | 39 | + * @param request 请求 |
| 40 | + * @return 提交结果 | ||
| 48 | */ | 41 | */ |
| 49 | - public SubmitResponse imagine(ImagineRequest imagineReqVO) { | ||
| 50 | - // 1、发送 post 请求 | ||
| 51 | - String res = post(URI_IMAGINE, imagineReqVO); | ||
| 52 | - // 2、转换 resp | ||
| 53 | - return JsonUtils.parseObject(res, SubmitResponse.class); | 42 | + public SubmitResponse imagine(ImagineRequest request) { |
| 43 | + String response = post("/submit/imagine", request); | ||
| 44 | + return JsonUtils.parseObject(response, SubmitResponse.class); | ||
| 54 | } | 45 | } |
| 55 | 46 | ||
| 56 | /** | 47 | /** |
| 57 | * action - 放大、缩小、U1、U2... | 48 | * action - 放大、缩小、U1、U2... |
| 58 | * | 49 | * |
| 59 | - * @param actionReqVO | 50 | + * @param request 请求 |
| 51 | + * @return 提交结果 | ||
| 60 | */ | 52 | */ |
| 61 | - public SubmitResponse action(ActionRequest actionReqVO) { | ||
| 62 | - // 1、发送 post 请求 | ||
| 63 | - String res = post(URI_ACTON, actionReqVO); | ||
| 64 | - // 2、转换 resp | 53 | + public SubmitResponse action(ActionRequest request) { |
| 54 | + String res = post("/submit/action", request); | ||
| 65 | return JsonUtils.parseObject(res, SubmitResponse.class); | 55 | return JsonUtils.parseObject(res, SubmitResponse.class); |
| 66 | } | 56 | } |
| 67 | 57 | ||
| 68 | /** | 58 | /** |
| 69 | * 批量查询 task 任务 | 59 | * 批量查询 task 任务 |
| 70 | * | 60 | * |
| 71 | - * @param taskIds | ||
| 72 | - * @return | 61 | + * @param ids 任务编号数组 |
| 62 | + * @return task 任务 | ||
| 73 | */ | 63 | */ |
| 74 | - public List<NotifyRequest> listByCondition(Collection<String> taskIds) { | ||
| 75 | - // 1、发送 post 请求 | ||
| 76 | - String res = post(URI_LIST_BY_CONDITION, ImmutableMap.of("ids", taskIds)); | ||
| 77 | - // 2、转换 对象 | ||
| 78 | - return JsonUtils.parseArray(res, NotifyRequest.class); | 64 | + public List<Notify> getTaskList(Collection<String> ids) { |
| 65 | + String res = post("/task/list-by-condition", ImmutableMap.of("ids", ids)); | ||
| 66 | + return JsonUtils.parseArray(res, Notify.class); | ||
| 79 | } | 67 | } |
| 80 | 68 | ||
| 81 | private String post(String uri, Object body) { | 69 | private String post(String uri, Object body) { |
| @@ -94,12 +82,12 @@ public class MidjourneyApi { | @@ -94,12 +82,12 @@ public class MidjourneyApi { | ||
| 94 | .block(); | 82 | .block(); |
| 95 | } | 83 | } |
| 96 | 84 | ||
| 97 | - // ====== record 结构 | 85 | + // ========== record 结构 ========== |
| 98 | 86 | ||
| 99 | /** | 87 | /** |
| 100 | - * Midjourney - Imagine 请求 | 88 | + * Imagine 请求(生成图片) |
| 101 | * | 89 | * |
| 102 | - * @param base64Array 垫图(参考图)base64数组 | 90 | + * @param base64Array 垫图(参考图) base64数 组 |
| 103 | * @param notifyHook 通知地址 | 91 | * @param notifyHook 通知地址 |
| 104 | * @param prompt 提示词 | 92 | * @param prompt 提示词 |
| 105 | * @param state 自定义参数 | 93 | * @param state 自定义参数 |
| @@ -108,10 +96,24 @@ public class MidjourneyApi { | @@ -108,10 +96,24 @@ public class MidjourneyApi { | ||
| 108 | String notifyHook, | 96 | String notifyHook, |
| 109 | String prompt, | 97 | String prompt, |
| 110 | String state) { | 98 | String state) { |
| 99 | + | ||
| 100 | + public static String buildState(Integer width, Integer height, String version, String model) { | ||
| 101 | + StringBuilder params = new StringBuilder(); | ||
| 102 | + // --ar 来设置尺寸 | ||
| 103 | + params.append(String.format(" --ar %s:%s ", width, height)); | ||
| 104 | + // --niji 模型 | ||
| 105 | + if (MidjourneyApi.ModelEnum.NIJI.getModel().equals(model)) { | ||
| 106 | + params.append(String.format(" --niji %s ", version)); | ||
| 107 | + } else { | ||
| 108 | + params.append(String.format(" --v %s ", version)); | ||
| 109 | + } | ||
| 110 | + return params.toString(); | ||
| 111 | + } | ||
| 112 | + | ||
| 111 | } | 113 | } |
| 112 | 114 | ||
| 113 | /** | 115 | /** |
| 114 | - * Midjourney - Action 请求 | 116 | + * Action 请求 |
| 115 | * | 117 | * |
| 116 | * @param customId 操作按钮id | 118 | * @param customId 操作按钮id |
| 117 | * @param taskId 操作按钮id | 119 | * @param taskId 操作按钮id |
| @@ -124,7 +126,7 @@ public class MidjourneyApi { | @@ -124,7 +126,7 @@ public class MidjourneyApi { | ||
| 124 | } | 126 | } |
| 125 | 127 | ||
| 126 | /** | 128 | /** |
| 127 | - * Midjourney - Submit 返回 | 129 | + * Submit 统一返回 |
| 128 | * | 130 | * |
| 129 | * @param code 状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误) | 131 | * @param code 状态码: 1(提交成功), 21(已存在), 22(排队中), other(错误) |
| 130 | * @param description 描述 | 132 | * @param description 描述 |
| @@ -138,7 +140,7 @@ public class MidjourneyApi { | @@ -138,7 +140,7 @@ public class MidjourneyApi { | ||
| 138 | } | 140 | } |
| 139 | 141 | ||
| 140 | /** | 142 | /** |
| 141 | - * Midjourney - 通知 request | 143 | + * 通知 request |
| 142 | * | 144 | * |
| 143 | * @param id job id | 145 | * @param id job id |
| 144 | * @param action 任务类型 {@link TaskActionEnum} | 146 | * @param action 任务类型 {@link TaskActionEnum} |
| @@ -155,47 +157,47 @@ public class MidjourneyApi { | @@ -155,47 +157,47 @@ public class MidjourneyApi { | ||
| 155 | * @param failReason 失败原因 | 157 | * @param failReason 失败原因 |
| 156 | * @param buttons 任务完成后的可执行按钮 | 158 | * @param buttons 任务完成后的可执行按钮 |
| 157 | */ | 159 | */ |
| 158 | - public record NotifyRequest(String id, | ||
| 159 | - String action, | ||
| 160 | - String status, | 160 | + public record Notify(String id, |
| 161 | + String action, | ||
| 162 | + String status, | ||
| 161 | 163 | ||
| 162 | - String prompt, | ||
| 163 | - String promptEn, | 164 | + String prompt, |
| 165 | + String promptEn, | ||
| 164 | 166 | ||
| 165 | - String description, | ||
| 166 | - String state, | 167 | + String description, |
| 168 | + String state, | ||
| 167 | 169 | ||
| 168 | - Long submitTime, | ||
| 169 | - Long startTime, | ||
| 170 | - Long finishTime, | 170 | + Long submitTime, |
| 171 | + Long startTime, | ||
| 172 | + Long finishTime, | ||
| 171 | 173 | ||
| 172 | - String imageUrl, | ||
| 173 | - String progress, | ||
| 174 | - String failReason, | ||
| 175 | - List<Button> buttons) { | 174 | + String imageUrl, |
| 175 | + String progress, | ||
| 176 | + String failReason, | ||
| 177 | + List<Button> buttons) { | ||
| 176 | 178 | ||
| 177 | - /** | ||
| 178 | - * button | ||
| 179 | - * | ||
| 180 | - * @param customId MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 | ||
| 181 | - * @param emoji 图标 emoji | ||
| 182 | - * @param label Make Variations 文本 | ||
| 183 | - * @param type 类型,系统内部使用 | ||
| 184 | - * @param style 样式: 2(Primary)、3(Green) | ||
| 185 | - */ | ||
| 186 | - public record Button(String customId, | ||
| 187 | - String emoji, | ||
| 188 | - String label, | ||
| 189 | - String type, | ||
| 190 | - String style) { | 179 | + } |
| 191 | 180 | ||
| 192 | - } | 181 | + /** |
| 182 | + * button | ||
| 183 | + * | ||
| 184 | + * @param customId MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识 | ||
| 185 | + * @param emoji 图标 emoji | ||
| 186 | + * @param label Make Variations 文本 | ||
| 187 | + * @param type 类型,系统内部使用 | ||
| 188 | + * @param style 样式: 2(Primary)、3(Green) | ||
| 189 | + */ | ||
| 190 | + public record Button(String customId, | ||
| 191 | + String emoji, | ||
| 192 | + String label, | ||
| 193 | + String type, | ||
| 194 | + String style) { | ||
| 193 | } | 195 | } |
| 194 | 196 | ||
| 195 | - // ====== enums | 197 | + // ============ enums ============ |
| 196 | 198 | ||
| 197 | /** | 199 | /** |
| 198 | - * Midjourney - 模型 | 200 | + * 模型枚举 |
| 199 | */ | 201 | */ |
| 200 | @AllArgsConstructor | 202 | @AllArgsConstructor |
| 201 | @Getter | 203 | @Getter |
| @@ -203,24 +205,15 @@ public class MidjourneyApi { | @@ -203,24 +205,15 @@ public class MidjourneyApi { | ||
| 203 | 205 | ||
| 204 | MIDJOURNEY("midjourney", "midjourney"), | 206 | MIDJOURNEY("midjourney", "midjourney"), |
| 205 | NIJI("niji", "niji"), | 207 | NIJI("niji", "niji"), |
| 206 | - | ||
| 207 | ; | 208 | ; |
| 208 | 209 | ||
| 209 | - private String model; | ||
| 210 | - private String name; | 210 | + private final String model; |
| 211 | + private final String name; | ||
| 211 | 212 | ||
| 212 | - public static ModelEnum valueOfModel(String model) { | ||
| 213 | - for (ModelEnum itemEnum : ModelEnum.values()) { | ||
| 214 | - if (itemEnum.getModel().equals(model)) { | ||
| 215 | - return itemEnum; | ||
| 216 | - } | ||
| 217 | - } | ||
| 218 | - throw new IllegalArgumentException("Invalid MessageType value: " + model); | ||
| 219 | - } | ||
| 220 | } | 213 | } |
| 221 | 214 | ||
| 222 | /** | 215 | /** |
| 223 | - * Midjourney - 提交返回的状态码 | 216 | + * 提交返回的状态码的枚举 |
| 224 | */ | 217 | */ |
| 225 | @Getter | 218 | @Getter |
| 226 | @AllArgsConstructor | 219 | @AllArgsConstructor |
| @@ -239,64 +232,68 @@ public class MidjourneyApi { | @@ -239,64 +232,68 @@ public class MidjourneyApi { | ||
| 239 | 232 | ||
| 240 | private final String code; | 233 | private final String code; |
| 241 | private final String name; | 234 | private final String name; |
| 235 | + | ||
| 242 | } | 236 | } |
| 243 | 237 | ||
| 244 | /** | 238 | /** |
| 245 | - * Midjourney - action | 239 | + * Action 枚举 |
| 246 | */ | 240 | */ |
| 247 | @Getter | 241 | @Getter |
| 248 | @AllArgsConstructor | 242 | @AllArgsConstructor |
| 249 | public enum TaskActionEnum { | 243 | public enum TaskActionEnum { |
| 244 | + | ||
| 250 | /** | 245 | /** |
| 251 | - * 生成图片. | 246 | + * 生成图片 |
| 252 | */ | 247 | */ |
| 253 | IMAGINE, | 248 | IMAGINE, |
| 254 | /** | 249 | /** |
| 255 | - * 选中放大. | 250 | + * 选中放大 |
| 256 | */ | 251 | */ |
| 257 | UPSCALE, | 252 | UPSCALE, |
| 258 | /** | 253 | /** |
| 259 | - * 选中其中的一张图,生成四张相似的. | 254 | + * 选中其中的一张图,生成四张相似的 |
| 260 | */ | 255 | */ |
| 261 | VARIATION, | 256 | VARIATION, |
| 262 | /** | 257 | /** |
| 263 | - * 重新执行. | 258 | + * 重新执行 |
| 264 | */ | 259 | */ |
| 265 | REROLL, | 260 | REROLL, |
| 266 | /** | 261 | /** |
| 267 | - * 图转prompt. | 262 | + * 图转 prompt |
| 268 | */ | 263 | */ |
| 269 | DESCRIBE, | 264 | DESCRIBE, |
| 270 | /** | 265 | /** |
| 271 | - * 多图混合. | 266 | + * 多图混合 |
| 272 | */ | 267 | */ |
| 273 | BLEND | 268 | BLEND |
| 269 | + | ||
| 274 | } | 270 | } |
| 275 | 271 | ||
| 276 | /** | 272 | /** |
| 277 | - * Midjourney - 任务状态 | 273 | + * 任务状态枚举 |
| 278 | */ | 274 | */ |
| 279 | @Getter | 275 | @Getter |
| 280 | @AllArgsConstructor | 276 | @AllArgsConstructor |
| 281 | public enum TaskStatusEnum { | 277 | public enum TaskStatusEnum { |
| 278 | + | ||
| 282 | /** | 279 | /** |
| 283 | - * 未启动. | 280 | + * 未启动 |
| 284 | */ | 281 | */ |
| 285 | NOT_START(0), | 282 | NOT_START(0), |
| 286 | /** | 283 | /** |
| 287 | - * 已提交. | 284 | + * 已提交 |
| 288 | */ | 285 | */ |
| 289 | SUBMITTED(1), | 286 | SUBMITTED(1), |
| 290 | /** | 287 | /** |
| 291 | - * 执行中. | 288 | + * 执行中 |
| 292 | */ | 289 | */ |
| 293 | IN_PROGRESS(3), | 290 | IN_PROGRESS(3), |
| 294 | /** | 291 | /** |
| 295 | - * 失败. | 292 | + * 失败 |
| 296 | */ | 293 | */ |
| 297 | FAILURE(4), | 294 | FAILURE(4), |
| 298 | /** | 295 | /** |
| 299 | - * 成功. | 296 | + * 成功 |
| 300 | */ | 297 | */ |
| 301 | SUCCESS(4); | 298 | SUCCESS(4); |
| 302 | 299 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/package-info.java
| @@ -2,11 +2,9 @@ | @@ -2,11 +2,9 @@ | ||
| 2 | * model 包,接入各种大模型,对标 https://github.com/spring-projects/spring-ai/tree/main/models | 2 | * model 包,接入各种大模型,对标 https://github.com/spring-projects/spring-ai/tree/main/models |
| 3 | * | 3 | * |
| 4 | * 1. yiyan 包:【百度】文心一言 | 4 | * 1. yiyan 包:【百度】文心一言 |
| 5 | - * 2. TODO 芋艿: | ||
| 6 | - * tongyi 包:【阿里】通义千问,对标 spring-cloud-alibaba 提供的 ai 包 | ||
| 7 | - * 2.2 | ||
| 8 | - * 2.3 xinghuo 包:【讯飞】星火,自己实现 | ||
| 9 | - * 2.4 openai 包:【OpenAI】ChatGPT,拷贝 spring-ai 提供的 models/openai 包 | ||
| 10 | - * 2.5 midjourney 包:Midjourney,参考 https://github.com/novicezk/midjourney-proxy 实现 | 5 | + * 2. tongyi 包:【阿里】通义千问,对标 spring-cloud-alibaba 提供的 ai 包 TODO 芋艿:未来直接使用它 |
| 6 | + * 3. xinghuo 包:【讯飞】星火,自己实现 | ||
| 7 | + * 4. midjourney 包:Midjourney,接入 https://github.com/novicezk/midjourney-proxy 实现 | ||
| 8 | + * 5. suno 包:TODO 芋艿: | ||
| 11 | */ | 9 | */ |
| 12 | package cn.iocoder.yudao.framework.ai.core.model; | 10 | package cn.iocoder.yudao.framework.ai.core.model; |
| 13 | \ No newline at end of file | 11 | \ No newline at end of file |
yudao-server/src/main/resources/application-local.yaml
| @@ -76,13 +76,6 @@ server: | @@ -76,13 +76,6 @@ server: | ||
| 76 | enabled: true | 76 | enabled: true |
| 77 | charset: UTF-8 | 77 | charset: UTF-8 |
| 78 | force: true | 78 | force: true |
| 79 | -# ai TODO @fan:这个融合到 yudao.ai 那好点哈 | ||
| 80 | -ai: | ||
| 81 | - midjourney-proxy: | ||
| 82 | - enable: true | ||
| 83 | - url: https://api.holdai.top/mj | ||
| 84 | - notifyUrl: http://61d61685.r21.cpolar.top/admin-api/ai/image/midjourney-notify | ||
| 85 | - key: sk-c3qxUCVKsPfdQiYU8440E3Fc8dE5424d9cB124A4Ee2489E3 | ||
| 86 | 79 | ||
| 87 | --- #################### 定时任务相关配置 #################### | 80 | --- #################### 定时任务相关配置 #################### |
| 88 | 81 |
yudao-server/src/main/resources/application.yaml
| @@ -201,6 +201,13 @@ yudao.ai: | @@ -201,6 +201,13 @@ yudao.ai: | ||
| 201 | enable: true | 201 | enable: true |
| 202 | base-url: https://suno-imrqwwui8-status2xxs-projects.vercel.app | 202 | base-url: https://suno-imrqwwui8-status2xxs-projects.vercel.app |
| 203 | 203 | ||
| 204 | +ai: | ||
| 205 | + midjourney-proxy: | ||
| 206 | + enable: true | ||
| 207 | + url: https://api.holdai.top/mj | ||
| 208 | + notifyUrl: http://61d61685.r21.cpolar.top/admin-api/ai/image/midjourney-notify | ||
| 209 | + key: sk-dZEPiVaNcT3FHhef51996bAa0bC74806BeAb620dA5Da10Bf | ||
| 210 | + | ||
| 204 | --- #################### 芋道相关配置 #################### | 211 | --- #################### 芋道相关配置 #################### |
| 205 | 212 | ||
| 206 | yudao: | 213 | yudao: |