浏览代码

【解决todo】Suno调用改为WebClient

xiaoxin 1 年之前
父节点
当前提交
a858eb84e5

+ 1 - 1
yudao-module-ai/yudao-module-ai-biz/src/main/java/cn/iocoder/yudao/module/ai/service/music/MusicServiceImpl.java

@@ -19,7 +19,7 @@ public class MusicServiceImpl implements MusicService {
 
     @Override
     public SunoRespVO musicGen(SunoReqVO sunoReqVO) {
-        SunoApi.SunoRequest req = BeanUtils.toBean(sunoReqVO, SunoApi.SunoRequest.class);
+        SunoApi.SunoReq req = BeanUtils.toBean(sunoReqVO, SunoApi.SunoReq.class);
         return BeanUtils.toBean(sunoApi.musicGen(req), SunoRespVO.class);
     }
 }

+ 0 - 1
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/config/YudaoAiProperties.java

@@ -137,7 +137,6 @@ public class YudaoAiProperties {
     }
 
     @Data
-    @Accessors(chain = true) // TODO @xiaoxin:可以去掉这个,默认全局已经开启
     public static class SunoProperties {
 
         private boolean enable = false;

+ 0 - 2
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/SunoConfig.java

@@ -3,14 +3,12 @@ package cn.iocoder.yudao.framework.ai.core.model.suno;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
-import lombok.experimental.Accessors;
 
 /**
  * @Author xiaoxin
  * @Date 2024/5/29
  */
 @Data
-@Accessors(chain = true)
 @NoArgsConstructor
 @AllArgsConstructor
 public class SunoConfig {

+ 29 - 44
yudao-module-ai/yudao-spring-boot-starter-ai/src/main/java/cn/iocoder/yudao/framework/ai/core/model/suno/api/SunoApi.java

@@ -1,65 +1,51 @@
 package cn.iocoder.yudao.framework.ai.core.model.suno.api;
 
 import cn.iocoder.yudao.framework.ai.core.model.suno.SunoConfig;
-import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import com.fasterxml.jackson.annotation.JsonInclude;
 import com.fasterxml.jackson.annotation.JsonProperty;
 import lombok.Data;
-import lombok.experimental.Accessors;
 import lombok.extern.slf4j.Slf4j;
-import okhttp3.*;
+import org.springframework.ai.openai.api.ApiUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+import reactor.core.publisher.Mono;
 
-import java.io.IOException;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
-// TODO @xiaoxin:类注释
 /**
+ * Suno API
+ * <br>
+ * 文档地址:https://platform.acedata.cloud/documents/d016ee3f-421b-4b6e-989a-8beba8701701
+ *
  * @Author xiaoxin
  * @Date 2024/5/27
  */
 @Slf4j
 public class SunoApi {
 
-    // TODO @xiaoxin:APPLICATION_JSON、TOKEN_PREFIX 看看 spring 有没自带的这 2 个枚举哈。变量越少越好
-    public static final String APPLICATION_JSON = "application/json";
-    public static final String TOKEN_PREFIX = "Bearer ";
-    public static final String API_URL = "https://api.acedata.cloud/suno/audios";
-
-    private static final int READ_TIMEOUT = 160; // 连接超时时间(秒),音乐生成时间较长,设置为 160s,后续可做callback
-
-    // TODO @xiaoxin:建议使用 webClient 对接。参考 https://github.com/spring-projects/spring-ai/blob/main/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/api/OpenAiApi.java
-    private final OkHttpClient client;
-
-    // TODO @xiaoxin:sunoConfig => config,简洁一点
-    public SunoApi(SunoConfig sunoConfig) {
-        this.client = new OkHttpClient().newBuilder().readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
-                .addInterceptor(chain -> {
-                    Request originalRequest = chain.request();
-                    Request requestWithUserAgent = originalRequest.newBuilder()
-                            .header("Authorization", TOKEN_PREFIX + sunoConfig.getToken())
-                            .build();
-                    return chain.proceed(requestWithUserAgent);
-                })
+    public static final String DEFAULT_BASE_URL = "https://api.acedata.cloud/suno";
+    private final WebClient webClient;
+
+    public SunoApi(SunoConfig config) {
+        this.webClient = WebClient.builder()
+                .baseUrl(DEFAULT_BASE_URL)
+                .defaultHeaders(ApiUtils.getJsonContentHeaders(config.getToken()))
                 .build();
     }
 
     // TODO @芋艿:方法名,要考虑下;
-    public SunoResponse musicGen(SunoRequest sunoRequest) {
-        Request request = new Request.Builder()
-                .url(API_URL)
-                .post(RequestBody.create(MediaType.parse(APPLICATION_JSON), JsonUtils.toJsonString(sunoRequest)))
-                .build();
-
-        try (Response response = client.newCall(request).execute()) {
-            if (!response.isSuccessful()) {
-                log.error("suno调用失败! response: {}", response);
-                throw new IllegalStateException("suno调用失败!" + response);
-            }
-            return JsonUtils.parseObject(response.body().string(), SunoResponse.class);
-        } catch (IOException ioException) {
-            throw new RuntimeException(ioException);
-        }
+    public SunoResp musicGen(SunoReq sunReq) {
+        return this.webClient.post()
+                .uri("/audios")
+                .body(Mono.just(sunReq), SunoReq.class)
+                .retrieve()
+                .onStatus(status -> !status.is2xxSuccessful(),
+                        response -> response.bodyToMono(String.class)
+                                .handle((respBody, sink) -> {
+                                    log.error("【Suno】调用失败!resp: 【{}】", respBody);
+                                    sink.error(new IllegalStateException("【Suno】调用失败!"));
+                                }))
+                .bodyToMono(SunoResp.class)
+                .block();
     }
 
     // TODO @xiaoxin:看看是不是使用 record 特性,简化下;
@@ -68,9 +54,8 @@ public class SunoApi {
      * 请求数据对象,用于生成音乐音频
      */
     @Data
-    @Accessors(chain = true)
     @JsonInclude(value = JsonInclude.Include.NON_NULL)
-    public static class SunoRequest {
+    public static class SunoReq {
         /**
          * 用于生成音乐音频的提示
          */
@@ -108,7 +93,7 @@ public class SunoApi {
      * SunoAPI 响应的数据
      */
     @Data
-    public static class SunoResponse {
+    public static class SunoResp {
         /**
          * 表示请求是否成功
          */

+ 4 - 5
yudao-module-ai/yudao-spring-boot-starter-ai/src/test/java/cn/iocoder/yudao/framework/ai/suno/SunoTests.java

@@ -15,18 +15,17 @@ public class SunoTests {
 
     @Before
     public void setup() {
-        String token = "13f13540dd3f4ae9885f63ac9f5d0b9f";
+        String token = "16b4356581984d538652354b60d69ff0";
         this.sunoConfig = new SunoConfig(token);
     }
 
     @Test
     public void generateMusic() {
         SunoApi sunoApi = new SunoApi(sunoConfig);
-        SunoApi.SunoRequest sunoRequest = new SunoApi
-                .SunoRequest()
+        SunoApi.SunoReq sunoReq = new SunoApi.SunoReq()
                 .setPrompt("创作一首带有轻松吉他旋律的流行歌曲,[verse] 描述夏日海滩的宁静,[chorus] 节奏加快,表达对自由的向往。");
-        SunoApi.SunoResponse sunoResponse = sunoApi.musicGen(sunoRequest);
-        System.out.println(sunoResponse);
+        SunoApi.SunoResp sunoResp = sunoApi.musicGen(sunoReq);
+        System.out.println(sunoResp);
     }
 
 }