Commit 4c89342d5bd700156a4d4a9f00e384bfa3785b5c
Merge remote-tracking branch 'origin/master-jdk21-ai' into master-jdk21-ai
Showing
11 changed files
with
67 additions
and
48 deletions
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/AiMusicStatusEnum.java
| @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.ai.enums; | @@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.ai.enums; | ||
| 3 | import lombok.AllArgsConstructor; | 3 | import lombok.AllArgsConstructor; |
| 4 | import lombok.Getter; | 4 | import lombok.Getter; |
| 5 | 5 | ||
| 6 | +// TODO @xin:这个类,挪到 enums/music 包下; | ||
| 7 | +// TODO @xin:1)@author 这个是标准的 javadoc;2)@date 可以不要哈;3)可以加下枚举类的注释 | ||
| 6 | /** | 8 | /** |
| 7 | * @Author xiaoxin | 9 | * @Author xiaoxin |
| 8 | * @Date 2024/6/5 | 10 | * @Date 2024/6/5 |
| @@ -11,6 +13,8 @@ import lombok.Getter; | @@ -11,6 +13,8 @@ import lombok.Getter; | ||
| 11 | @Getter | 13 | @Getter |
| 12 | public enum AiMusicStatusEnum { | 14 | public enum AiMusicStatusEnum { |
| 13 | 15 | ||
| 16 | + // TODO @xin:是不是收敛成,只有 3 个:进行中,成功,失败;类似 AiImageStatusEnum | ||
| 17 | + | ||
| 14 | SUBMITTED("submitted", "已提交"), | 18 | SUBMITTED("submitted", "已提交"), |
| 15 | QUEUED("queued", "排队中"), | 19 | QUEUED("queued", "排队中"), |
| 16 | STREAMING("streaming", "进行中"), | 20 | STREAMING("streaming", "进行中"), |
yudao-module-ai/yudao-module-ai-api/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiModelEnum.java
| @@ -50,6 +50,7 @@ public enum AiModelEnum { | @@ -50,6 +50,7 @@ public enum AiModelEnum { | ||
| 50 | XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"), | 50 | XING_HUO_3_0("星火大模型3.0", "generalv3", "/v3.1/chat"), |
| 51 | XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"), | 51 | XING_HUO_3_5("星火大模型3.5", "generalv3.5", "/v3.5/chat"), |
| 52 | 52 | ||
| 53 | + // TODO @xin:// Suno;中间加个空格,会更清晰一点。一般来说,不同类型的单词之间,最好有空格。例如说,// 新增一个;再例如说;// 这是 1 个 create 逻辑 | ||
| 53 | //Suno | 54 | //Suno |
| 54 | SUNO_2( "SUNO-2", "chirp-v2-xxl-alpha",null), | 55 | SUNO_2( "SUNO-2", "chirp-v2-xxl-alpha",null), |
| 55 | SUNO_3_0( "SUNO-3.0", "chirp-v3-0",null), | 56 | SUNO_3_0( "SUNO-3.0", "chirp-v3-0",null), |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/MusicController.java
| @@ -17,6 +17,7 @@ import java.util.List; | @@ -17,6 +17,7 @@ import java.util.List; | ||
| 17 | 17 | ||
| 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; | 18 | import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; |
| 19 | 19 | ||
| 20 | +// TODO @xin:AI 前缀;都要加下哈 | ||
| 20 | @Tag(name = "管理后台 - AI 音乐生成") | 21 | @Tag(name = "管理后台 - AI 音乐生成") |
| 21 | @RestController | 22 | @RestController |
| 22 | @RequestMapping("/ai/music") | 23 | @RequestMapping("/ai/music") |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/controller/admin/music/vo/SunoReqVO.java
| @@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; | @@ -4,12 +4,13 @@ import com.fasterxml.jackson.annotation.JsonInclude; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | 5 | ||
| 6 | @Data | 6 | @Data |
| 7 | -@JsonInclude(value = JsonInclude.Include.NON_NULL) | 7 | +@JsonInclude(value = JsonInclude.Include.NON_NULL) // TODO @xin:不用加这个哈 |
| 8 | public class SunoReqVO { | 8 | public class SunoReqVO { |
| 9 | /** | 9 | /** |
| 10 | * 用于生成音乐音频的提示 | 10 | * 用于生成音乐音频的提示 |
| 11 | */ | 11 | */ |
| 12 | private String prompt; | 12 | private String prompt; |
| 13 | + // TODO @xin:Boolean,不使用基本类型。 | ||
| 13 | /** | 14 | /** |
| 14 | * 是否纯音乐 | 15 | * 是否纯音乐 |
| 15 | */ | 16 | */ |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/dataobject/music/AiMusicDO.java
| @@ -18,6 +18,8 @@ import java.util.stream.Collectors; | @@ -18,6 +18,8 @@ import java.util.stream.Collectors; | ||
| 18 | @TableName("ai_music") | 18 | @TableName("ai_music") |
| 19 | @Data | 19 | @Data |
| 20 | public class AiMusicDO extends BaseDO { | 20 | public class AiMusicDO extends BaseDO { |
| 21 | + | ||
| 22 | + // TODO @xin:@Schema 只在 VO 里使用,这里还是使用标准的注释哈 | ||
| 21 | @TableId(type = IdType.AUTO) | 23 | @TableId(type = IdType.AUTO) |
| 22 | @Schema(description = "编号") | 24 | @Schema(description = "编号") |
| 23 | private Long id; | 25 | private Long id; |
| @@ -40,6 +42,7 @@ public class AiMusicDO extends BaseDO { | @@ -40,6 +42,7 @@ public class AiMusicDO extends BaseDO { | ||
| 40 | @Schema(description = "视频地址") | 42 | @Schema(description = "视频地址") |
| 41 | private String videoUrl; | 43 | private String videoUrl; |
| 42 | 44 | ||
| 45 | + // TODO @xin:需要关联下对应的枚举 | ||
| 43 | @Schema(description = "音乐状态") | 46 | @Schema(description = "音乐状态") |
| 44 | private String status; | 47 | private String status; |
| 45 | 48 | ||
| @@ -49,19 +52,24 @@ public class AiMusicDO extends BaseDO { | @@ -49,19 +52,24 @@ public class AiMusicDO extends BaseDO { | ||
| 49 | @Schema(description = "提示词") | 52 | @Schema(description = "提示词") |
| 50 | private String prompt; | 53 | private String prompt; |
| 51 | 54 | ||
| 55 | + // TODO @xin:生成模式,需要记录下;歌词、描述 | ||
| 56 | + | ||
| 57 | + // TODO @xin:多存储一个平台,platform;考虑未来可能有别的音乐接口 | ||
| 52 | @Schema(description = "模型") | 58 | @Schema(description = "模型") |
| 53 | private String model; | 59 | private String model; |
| 54 | 60 | ||
| 55 | @Schema(description = "错误信息") | 61 | @Schema(description = "错误信息") |
| 56 | private String errorMessage; | 62 | private String errorMessage; |
| 57 | 63 | ||
| 64 | + // TODO @xin:tags 要不要使用 List<String> | ||
| 65 | + | ||
| 58 | @Schema(description = "音乐风格标签") | 66 | @Schema(description = "音乐风格标签") |
| 59 | private String tags; | 67 | private String tags; |
| 60 | 68 | ||
| 61 | - @Schema(description = "任务id") | 69 | + @Schema(description = "任务编号") |
| 62 | private String taskId; | 70 | private String taskId; |
| 63 | 71 | ||
| 64 | - | 72 | + // TODO @xin:转换不放在 DO 里面哈。 |
| 65 | 73 | ||
| 66 | public static AiMusicDO convertFrom(SunoApi.MusicData musicData) { | 74 | public static AiMusicDO convertFrom(SunoApi.MusicData musicData) { |
| 67 | return new AiMusicDO() | 75 | return new AiMusicDO() |
| @@ -84,5 +92,4 @@ public class AiMusicDO extends BaseDO { | @@ -84,5 +92,4 @@ public class AiMusicDO extends BaseDO { | ||
| 84 | .collect(Collectors.toList()); | 92 | .collect(Collectors.toList()); |
| 85 | } | 93 | } |
| 86 | 94 | ||
| 87 | - | ||
| 88 | } | 95 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/dal/mysql/music/AiMusicMapper.java
| @@ -5,10 +5,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; | @@ -5,10 +5,9 @@ import cn.iocoder.yudao.module.ai.dal.dataobject.music.AiMusicDO; | ||
| 5 | import org.apache.ibatis.annotations.Mapper; | 5 | import org.apache.ibatis.annotations.Mapper; |
| 6 | 6 | ||
| 7 | /** | 7 | /** |
| 8 | - * @Author xiaoxin | ||
| 9 | - * @Date 2024/6/5 | 8 | + * AI 音乐 Mapper |
| 9 | + * @author xiaoxin | ||
| 10 | */ | 10 | */ |
| 11 | @Mapper | 11 | @Mapper |
| 12 | public interface AiMusicMapper extends BaseMapperX<AiMusicDO> { | 12 | public interface AiMusicMapper extends BaseMapperX<AiMusicDO> { |
| 13 | - | ||
| 14 | } | 13 | } |
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java
| @@ -31,26 +31,29 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti | @@ -31,26 +31,29 @@ import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUti | ||
| 31 | @Slf4j | 31 | @Slf4j |
| 32 | public class MusicServiceImpl implements MusicService { | 32 | public class MusicServiceImpl implements MusicService { |
| 33 | 33 | ||
| 34 | + // TODO @xin:使用 @Resource 注入,整个项目保持统一哈; | ||
| 34 | private final SunoApi sunoApi; | 35 | private final SunoApi sunoApi; |
| 35 | private final AiMusicMapper musicMapper; | 36 | private final AiMusicMapper musicMapper; |
| 36 | 37 | ||
| 37 | private final Queue<String> taskQueue = new ConcurrentLinkedQueue<>(); | 38 | private final Queue<String> taskQueue = new ConcurrentLinkedQueue<>(); |
| 38 | 39 | ||
| 40 | + // TODO @xin:要不把 descriptionMode、lyricMode 合并,同一个 generateMusic 方法,然后根据传入的 mode 模式:歌词、描述来区分? | ||
| 39 | 41 | ||
| 40 | @Override | 42 | @Override |
| 41 | public List<Long> descriptionMode(SunoReqVO reqVO) { | 43 | public List<Long> descriptionMode(SunoReqVO reqVO) { |
| 42 | - SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.isMakeInstrumental()); | ||
| 43 | - //默认异步 | 44 | + // 1. 异步生成 |
| 45 | + SunoApi.SunoRequest sunoReq = new SunoApi.SunoRequest(reqVO.getPrompt(), reqVO.getMv(), reqVO.isMakeInstrumental()); | ||
| 44 | List<SunoApi.MusicData> musicDataList = sunoApi.generate(sunoReq); | 46 | List<SunoApi.MusicData> musicDataList = sunoApi.generate(sunoReq); |
| 47 | + // 2. 插入数据库 | ||
| 45 | return insertMusicData(musicDataList); | 48 | return insertMusicData(musicDataList); |
| 46 | } | 49 | } |
| 47 | 50 | ||
| 48 | - | ||
| 49 | @Override | 51 | @Override |
| 50 | public List<Long> lyricMode(SunoLyricModeVO reqVO) { | 52 | public List<Long> lyricMode(SunoLyricModeVO reqVO) { |
| 51 | - SunoApi.SunoReq sunoReq = new SunoApi.SunoReq(reqVO.getPrompt(), reqVO.getMv(), reqVO.getTags(), reqVO.getTitle()); | ||
| 52 | - //默认异步 | 53 | + // 1. 异步生成 |
| 54 | + SunoApi.SunoRequest sunoReq = new SunoApi.SunoRequest(reqVO.getPrompt(), reqVO.getMv(), reqVO.getTags(), reqVO.getTitle()); | ||
| 53 | List<SunoApi.MusicData> musicDataList = sunoApi.customGenerate(sunoReq); | 55 | List<SunoApi.MusicData> musicDataList = sunoApi.customGenerate(sunoReq); |
| 56 | + // 2. 插入数据库 | ||
| 54 | return insertMusicData(musicDataList); | 57 | return insertMusicData(musicDataList); |
| 55 | } | 58 | } |
| 56 | 59 | ||
| @@ -64,6 +67,7 @@ public class MusicServiceImpl implements MusicService { | @@ -64,6 +67,7 @@ public class MusicServiceImpl implements MusicService { | ||
| 64 | if (CollUtil.isEmpty(musicDataList)) { | 67 | if (CollUtil.isEmpty(musicDataList)) { |
| 65 | return Collections.emptyList(); | 68 | return Collections.emptyList(); |
| 66 | } | 69 | } |
| 70 | + // TODO @xin:建议使用 insertBatch 方法,批量插入 | ||
| 67 | return AiMusicDO.convertFrom(musicDataList).stream() | 71 | return AiMusicDO.convertFrom(musicDataList).stream() |
| 68 | .peek(musicDO -> musicMapper.insert(musicDO.setUserId(getLoginUserId()))) | 72 | .peek(musicDO -> musicMapper.insert(musicDO.setUserId(getLoginUserId()))) |
| 69 | .peek(e -> Optional.of(e.getTaskId()).ifPresent(taskQueue::add)) | 73 | .peek(e -> Optional.of(e.getTaskId()).ifPresent(taskQueue::add)) |
| @@ -71,6 +75,7 @@ public class MusicServiceImpl implements MusicService { | @@ -71,6 +75,7 @@ public class MusicServiceImpl implements MusicService { | ||
| 71 | .collect(Collectors.toList()); | 75 | .collect(Collectors.toList()); |
| 72 | } | 76 | } |
| 73 | 77 | ||
| 78 | + // TODO @xin:这个,改成标准的 job 来实现哈。从数据库加载任务,然后执行。 | ||
| 74 | @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS) | 79 | @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS) |
| 75 | @Transactional | 80 | @Transactional |
| 76 | public void flushSunoTask() { | 81 | public void flushSunoTask() { |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java
| @@ -118,8 +118,9 @@ public class YudaoAiProperties { | @@ -118,8 +118,9 @@ public class YudaoAiProperties { | ||
| 118 | public static class SunoProperties { | 118 | public static class SunoProperties { |
| 119 | 119 | ||
| 120 | private boolean enable = false; | 120 | private boolean enable = false; |
| 121 | + | ||
| 121 | /** | 122 | /** |
| 122 | - * suno-api 服务的基本地址 | 123 | + * API 服务的基本地址 |
| 123 | */ | 124 | */ |
| 124 | private String baseUrl; | 125 | private String baseUrl; |
| 125 | 126 |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/SunoConfig.java
| @@ -4,16 +4,20 @@ import lombok.AllArgsConstructor; | @@ -4,16 +4,20 @@ import lombok.AllArgsConstructor; | ||
| 4 | import lombok.Data; | 4 | import lombok.Data; |
| 5 | import lombok.NoArgsConstructor; | 5 | import lombok.NoArgsConstructor; |
| 6 | 6 | ||
| 7 | +// TODO @xin:不需要这个类哈,直接 SunoApi 传入 baseUrl 参数即可 | ||
| 7 | /** | 8 | /** |
| 8 | - * @Author xiaoxin | ||
| 9 | - * @Date 2024/5/29 | 9 | + * Suno 配置类 |
| 10 | + * | ||
| 11 | + * @author xiaoxin | ||
| 10 | */ | 12 | */ |
| 11 | @Data | 13 | @Data |
| 12 | @NoArgsConstructor | 14 | @NoArgsConstructor |
| 13 | @AllArgsConstructor | 15 | @AllArgsConstructor |
| 14 | public class SunoConfig { | 16 | public class SunoConfig { |
| 17 | + | ||
| 15 | /** | 18 | /** |
| 16 | * suno-api服务的基本路径 | 19 | * suno-api服务的基本路径 |
| 17 | */ | 20 | */ |
| 18 | private String baseUrl; | 21 | private String baseUrl; |
| 22 | + | ||
| 19 | } | 23 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java
| @@ -27,14 +27,15 @@ import java.util.function.Predicate; | @@ -27,14 +27,15 @@ import java.util.function.Predicate; | ||
| 27 | public class SunoApi { | 27 | public class SunoApi { |
| 28 | 28 | ||
| 29 | private final WebClient webClient; | 29 | private final WebClient webClient; |
| 30 | + | ||
| 30 | private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful(); | 31 | private final Predicate<HttpStatusCode> STATUS_PREDICATE = status -> !status.is2xxSuccessful(); |
| 31 | private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class) | 32 | private final Function<ClientResponse, Mono<? extends Throwable>> EXCEPTION_FUNCTION = response -> response.bodyToMono(String.class) |
| 32 | .handle((respBody, sink) -> { | 33 | .handle((respBody, sink) -> { |
| 34 | + // TODO @xin:最好是 request、response 都有哈 | ||
| 33 | log.error("【suno-api】调用失败!resp: 【{}】", respBody); | 35 | log.error("【suno-api】调用失败!resp: 【{}】", respBody); |
| 34 | sink.error(new IllegalStateException("【suno-api】调用失败!")); | 36 | sink.error(new IllegalStateException("【suno-api】调用失败!")); |
| 35 | }); | 37 | }); |
| 36 | 38 | ||
| 37 | - | ||
| 38 | public SunoApi(SunoConfig config) { | 39 | public SunoApi(SunoConfig config) { |
| 39 | this.webClient = WebClient.builder() | 40 | this.webClient = WebClient.builder() |
| 40 | .baseUrl(config.getBaseUrl()) | 41 | .baseUrl(config.getBaseUrl()) |
| @@ -42,50 +43,49 @@ public class SunoApi { | @@ -42,50 +43,49 @@ public class SunoApi { | ||
| 42 | .build(); | 43 | .build(); |
| 43 | } | 44 | } |
| 44 | 45 | ||
| 45 | - public List<MusicData> generate(SunoApi.SunoReq sunReq) { | 46 | + public List<MusicData> generate(SunoRequest request) { |
| 46 | return this.webClient.post() | 47 | return this.webClient.post() |
| 47 | .uri("/api/generate") | 48 | .uri("/api/generate") |
| 48 | - .body(Mono.just(sunReq), SunoApi.SunoReq.class) | 49 | + .body(Mono.just(request), SunoRequest.class) |
| 49 | .retrieve() | 50 | .retrieve() |
| 50 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | 51 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) |
| 51 | - .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | ||
| 52 | - }) | 52 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { }) |
| 53 | .block(); | 53 | .block(); |
| 54 | } | 54 | } |
| 55 | 55 | ||
| 56 | - public List<MusicData> customGenerate(SunoApi.SunoReq sunReq) { | 56 | + public List<MusicData> customGenerate(SunoRequest request) { |
| 57 | return this.webClient.post() | 57 | return this.webClient.post() |
| 58 | .uri("/api/custom_generate") | 58 | .uri("/api/custom_generate") |
| 59 | - .body(Mono.just(sunReq), SunoApi.SunoReq.class) | 59 | + .body(Mono.just(request), SunoRequest.class) |
| 60 | .retrieve() | 60 | .retrieve() |
| 61 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | 61 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) |
| 62 | - .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | ||
| 63 | - }) | 62 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { }) |
| 64 | .block(); | 63 | .block(); |
| 65 | } | 64 | } |
| 66 | 65 | ||
| 66 | + // TODO @xin: 是不是叫 chatCompletion | ||
| 67 | public List<MusicData> doChatCompletion(String prompt) { | 67 | public List<MusicData> doChatCompletion(String prompt) { |
| 68 | return this.webClient.post() | 68 | return this.webClient.post() |
| 69 | .uri("/v1/chat/completions") | 69 | .uri("/v1/chat/completions") |
| 70 | - .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class) | 70 | + .body(Mono.just(new SunoRequest(prompt)), SunoRequest.class) |
| 71 | .retrieve() | 71 | .retrieve() |
| 72 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | 72 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) |
| 73 | - .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | ||
| 74 | - }) | 73 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { }) |
| 75 | .block(); | 74 | .block(); |
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | public LyricsData generateLyrics(String prompt) { | 77 | public LyricsData generateLyrics(String prompt) { |
| 79 | return this.webClient.post() | 78 | return this.webClient.post() |
| 80 | .uri("/api/generate_lyrics") | 79 | .uri("/api/generate_lyrics") |
| 81 | - .body(Mono.just(new SunoReq(prompt)), SunoApi.SunoReq.class) | 80 | + .body(Mono.just(new SunoRequest(prompt)), SunoRequest.class) |
| 82 | .retrieve() | 81 | .retrieve() |
| 83 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | 82 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) |
| 84 | .bodyToMono(LyricsData.class) | 83 | .bodyToMono(LyricsData.class) |
| 85 | .block(); | 84 | .block(); |
| 86 | } | 85 | } |
| 87 | 86 | ||
| 88 | - | 87 | + // TODO @xin:应该传入 List<String> ids |
| 88 | + // TODO @xin:方法名,建议使用 getMusicList | ||
| 89 | public List<MusicData> selectById(String ids) { | 89 | public List<MusicData> selectById(String ids) { |
| 90 | return this.webClient.get() | 90 | return this.webClient.get() |
| 91 | .uri(uriBuilder -> uriBuilder | 91 | .uri(uriBuilder -> uriBuilder |
| @@ -94,12 +94,11 @@ public class SunoApi { | @@ -94,12 +94,11 @@ public class SunoApi { | ||
| 94 | .build()) | 94 | .build()) |
| 95 | .retrieve() | 95 | .retrieve() |
| 96 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) | 96 | .onStatus(STATUS_PREDICATE, EXCEPTION_FUNCTION) |
| 97 | - .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { | ||
| 98 | - }) | 97 | + .bodyToMono(new ParameterizedTypeReference<List<MusicData>>() { }) |
| 99 | .block(); | 98 | .block(); |
| 100 | } | 99 | } |
| 101 | 100 | ||
| 102 | - | 101 | + // TODO @xin:方法名,建议使用 getLimitUsage |
| 103 | public LimitData selectLimit() { | 102 | public LimitData selectLimit() { |
| 104 | return this.webClient.get() | 103 | return this.webClient.get() |
| 105 | .uri("/api/get_limit") | 104 | .uri("/api/get_limit") |
| @@ -109,7 +108,7 @@ public class SunoApi { | @@ -109,7 +108,7 @@ public class SunoApi { | ||
| 109 | .block(); | 108 | .block(); |
| 110 | } | 109 | } |
| 111 | 110 | ||
| 112 | - | 111 | + // TODO @xin:可以改成 MusicGenerateRequest |
| 113 | /** | 112 | /** |
| 114 | * 根据提示生成音频 | 113 | * 根据提示生成音频 |
| 115 | * | 114 | * |
| @@ -117,12 +116,12 @@ public class SunoApi { | @@ -117,12 +116,12 @@ public class SunoApi { | ||
| 117 | * @param tags 音乐风格 | 116 | * @param tags 音乐风格 |
| 118 | * @param title 音乐名称 | 117 | * @param title 音乐名称 |
| 119 | * @param mv 模型 | 118 | * @param mv 模型 |
| 120 | - * @param waitAudio false表示后台模式,仅返回音频任务信息,需要调用get API获取详细的音频信息。 | ||
| 121 | - * true表示同步模式,API最多等待100s,音频生成完毕后直接返回音频链接等信息,建议在GPT等agent中使用。 | 119 | + * @param waitAudio false 表示后台模式,仅返回音频任务信息,需要调用 get API 获取详细的音频信息。 |
| 120 | + * true 表示同步模式,API 最多等待 100s,音频生成完毕后直接返回音频链接等信息,建议在 GPT 等 agent 中使用。 | ||
| 122 | * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 | 121 | * @param makeInstrumental 指示音乐音频是否为定制,如果为 true,则从歌词生成,否则从提示生成 |
| 123 | */ | 122 | */ |
| 124 | @JsonInclude(value = JsonInclude.Include.NON_NULL) | 123 | @JsonInclude(value = JsonInclude.Include.NON_NULL) |
| 125 | - public record SunoReq( | 124 | + public record SunoRequest( |
| 126 | String prompt, | 125 | String prompt, |
| 127 | String tags, | 126 | String tags, |
| 128 | String title, | 127 | String title, |
| @@ -130,23 +129,23 @@ public class SunoApi { | @@ -130,23 +129,23 @@ public class SunoApi { | ||
| 130 | @JsonProperty("wait_audio") boolean waitAudio, | 129 | @JsonProperty("wait_audio") boolean waitAudio, |
| 131 | @JsonProperty("make_instrumental") boolean makeInstrumental | 130 | @JsonProperty("make_instrumental") boolean makeInstrumental |
| 132 | ) { | 131 | ) { |
| 133 | - public SunoReq(String prompt) { | 132 | + |
| 133 | + public SunoRequest(String prompt) { | ||
| 134 | this(prompt, null, null, null, false, false); | 134 | this(prompt, null, null, null, false, false); |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | - public SunoReq(String prompt, String mv, boolean makeInstrumental) { | 137 | + public SunoRequest(String prompt, String mv, boolean makeInstrumental) { |
| 138 | this(prompt, null, null, mv, false, makeInstrumental); | 138 | this(prompt, null, null, mv, false, makeInstrumental); |
| 139 | } | 139 | } |
| 140 | 140 | ||
| 141 | - | ||
| 142 | - public SunoReq(String prompt, String mv, String tags, String title) { | 141 | + public SunoRequest(String prompt, String mv, String tags, String title) { |
| 143 | this(prompt, tags, title, mv, false, false); | 142 | this(prompt, tags, title, mv, false, false); |
| 144 | } | 143 | } |
| 145 | - } | ||
| 146 | 144 | ||
| 145 | + } | ||
| 147 | 146 | ||
| 148 | /** | 147 | /** |
| 149 | - * SunoAPI 响应的音频数据。 | 148 | + * Suno API 响应的音频数据 |
| 150 | * | 149 | * |
| 151 | * @param id 音乐数据的 ID | 150 | * @param id 音乐数据的 ID |
| 152 | * @param title 音乐音频的标题 | 151 | * @param title 音乐音频的标题 |
| @@ -179,9 +178,8 @@ public class SunoApi { | @@ -179,9 +178,8 @@ public class SunoApi { | ||
| 179 | ) { | 178 | ) { |
| 180 | } | 179 | } |
| 181 | 180 | ||
| 182 | - | ||
| 183 | /** | 181 | /** |
| 184 | - * SunoAPI 响应的歌词数据。 | 182 | + * Suno API 响应的歌词数据。 |
| 185 | * | 183 | * |
| 186 | * @param text 歌词 | 184 | * @param text 歌词 |
| 187 | * @param title 标题 | 185 | * @param title 标题 |
| @@ -194,9 +192,8 @@ public class SunoApi { | @@ -194,9 +192,8 @@ public class SunoApi { | ||
| 194 | ) { | 192 | ) { |
| 195 | } | 193 | } |
| 196 | 194 | ||
| 197 | - | ||
| 198 | /** | 195 | /** |
| 199 | - * SunoAPI 响应的限额数据,目前每日免费50 | 196 | + * Suno API 响应的限额数据,目前每日免费50 |
| 200 | */ | 197 | */ |
| 201 | public record LimitData( | 198 | public record LimitData( |
| 202 | @JsonProperty("credits_left") Long creditsLeft, | 199 | @JsonProperty("credits_left") Long creditsLeft, |
| @@ -206,5 +203,4 @@ public class SunoApi { | @@ -206,5 +203,4 @@ public class SunoApi { | ||
| 206 | ) { | 203 | ) { |
| 207 | } | 204 | } |
| 208 | 205 | ||
| 209 | - | ||
| 210 | } | 206 | } |
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java
| @@ -29,7 +29,7 @@ public class SunoTests { | @@ -29,7 +29,7 @@ public class SunoTests { | ||
| 29 | 29 | ||
| 30 | @Test | 30 | @Test |
| 31 | public void generate() { | 31 | public void generate() { |
| 32 | - List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoReq("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")); | 32 | + List<SunoApi.MusicData> generate = sunoApi.generate(new SunoApi.SunoRequest("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。")); |
| 33 | System.out.println(generate); | 33 | System.out.println(generate); |
| 34 | } | 34 | } |
| 35 | 35 |