diff --git a/pom.xml b/pom.xml index 16004b1..5c61eb3 100644 --- a/pom.xml +++ b/pom.xml @@ -353,6 +353,24 @@ 3.4.0 + + + cn.xfyun + websdk-java-spark + 2.1.3 + + + + cn.xfyun + websdk-java-speech + 3.0.3 + + + + cn.xfyun + websdk-java-nlp + 2.0.7 + diff --git a/src/main/java/com/newyetai/ai/controller/AiController.java b/src/main/java/com/newyetai/ai/controller/AiController.java index b2bcde8..919d51e 100644 --- a/src/main/java/com/newyetai/ai/controller/AiController.java +++ b/src/main/java/com/newyetai/ai/controller/AiController.java @@ -1,6 +1,7 @@ package com.newyetai.ai.controller; import com.newyetai.common.AlitaResult; +import com.newyetai.sdk.xf.dto.RtasrReqDTO; import com.newyetai.sdk.xf.model.ChatReq; import com.newyetai.sdk.xf.model.TranslateReq; import com.newyetai.sdk.xf.util.XfUtils; @@ -25,9 +26,15 @@ public class AiController { @PostMapping("translate") @ApiOperation("ai翻译") - public AlitaResult chat(@Validated @RequestBody TranslateReq req) { + public AlitaResult translate(@Validated @RequestBody TranslateReq req) { return AlitaResult.success(XfUtils.translate(req)); } + @PostMapping("rtasr") + @ApiOperation("ai实时语音转写") + public AlitaResult rtasr(@Validated @RequestBody RtasrReqDTO req) { + return AlitaResult.success(XfUtils.rtasr(req)); + } + } diff --git a/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java b/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java index 827027c..5937ba2 100644 --- a/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java +++ b/src/main/java/com/newyetai/sdk/xf/config/XfProperties.java @@ -36,6 +36,16 @@ public class XfProperties implements Serializable{ private String ttsHostUrl; /** 机器翻译niutrans **/ private String otsHostUrl; + /** 实时语音转写 **/ + private XfAppProperties rtasr; + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class XfAppProperties implements Serializable{ + private String apiKey; + } } diff --git a/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java b/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java new file mode 100644 index 0000000..b5b1399 --- /dev/null +++ b/src/main/java/com/newyetai/sdk/xf/dto/RtasrReqDTO.java @@ -0,0 +1,24 @@ +package com.newyetai.sdk.xf.dto; + +import java.io.Serializable; +import javax.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Builder.Default; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class RtasrReqDTO implements Serializable { + + @NotBlank(message = "语音文件不能为空") + private String fileCode; + @Default + private String from = "cn_uyghur"; + + + +} diff --git a/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java b/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java index cd9c244..3927708 100644 --- a/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java +++ b/src/main/java/com/newyetai/sdk/xf/model/ChatReq.java @@ -22,8 +22,19 @@ public class ChatReq implements Serializable{ @Singular @NotNull(message = "消息内容不能为空") private List messages; - - - + @Default + private String temperature = "0.3"; + @Default + private String max_tokens = "150"; + @Default + private String repetition_penalty = "1.25"; + @Default + private String top_p = "0.65"; + @Default + private String presence_penalty = "-0.6"; + @Default + private String frequency_penalty = "0.2"; + + } diff --git a/src/main/java/com/newyetai/sdk/xf/service/RtasrClientApp.java b/src/main/java/com/newyetai/sdk/xf/service/RtasrClientApp.java new file mode 100644 index 0000000..a98dbda --- /dev/null +++ b/src/main/java/com/newyetai/sdk/xf/service/RtasrClientApp.java @@ -0,0 +1,155 @@ +package com.newyetai.sdk.xf.service; + +import cn.hutool.json.JSON; +import cn.hutool.json.JSONArray; +import cn.hutool.json.JSONObject; +import cn.hutool.json.JSONUtil; +import cn.xfyun.api.RtasrClient; +import cn.xfyun.model.response.rtasr.RtasrResponse; +import cn.xfyun.service.rta.AbstractRtasrWebSocketListener; +import com.newyetai.common.AlitaResult; +import com.newyetai.common.util.JsonUtils; +import com.newyetai.sdk.xf.config.XfProperties; +import java.io.InputStream; +import java.util.concurrent.CountDownLatch; +import okhttp3.Response; +import okhttp3.WebSocket; +import org.apache.commons.codec.binary.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ( Real-time ASR ) 实时语音转写 1、APPID、APIKey信息获取:... 2、文档地址:... + * + * @author kaili23 + */ +public class RtasrClientApp extends AbstractRtasrWebSocketListener{ + + private static final Logger logger = LoggerFactory.getLogger(RtasrClientApp.class); + + /** + * 记录完整结果 + */ + private StringBuffer finalResult = new StringBuffer(); + + private AlitaResult result; + + /** + * 线程同步 + */ + private CountDownLatch latch; + + RtasrClientApp(CountDownLatch latch){ + this.latch = latch; + } + + public AlitaResult getResult() { + return result; + } + + @Override + public void onSuccess(WebSocket webSocket, String text) { + RtasrResponse response = JsonUtils.toObject(text, RtasrResponse.class); + // finalResult为服务端已转写的完整语句,后面拼接服务端本次返回的当前语句的转写中间结果。 + String tempResult = handleAndReturnContent(response.getData()); + logger.info("实时转写结果:{}", finalResult + tempResult); + result = AlitaResult.success(finalResult.toString()); + } + + @Override + public void onFail(WebSocket webSocket, Throwable t, Response response) { + result = AlitaResult.bizError(t.getMessage()); + latch.countDown(); + System.exit(0); + } + + @Override + public void onBusinessFail(WebSocket webSocket, String text) { + logger.error("业务异常,返回信息为:{}", text); + result = AlitaResult.bizError(text); + latch.countDown(); + System.exit(0); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + latch.countDown(); + System.exit(0); + } + + @Override + public void onClosed() { + latch.countDown(); + System.exit(0); + } + + public static AlitaResult execute(InputStream is, String from, XfProperties xfProperties) { + try { + CountDownLatch latch = new CountDownLatch(1); + RtasrClientApp app = new RtasrClientApp(latch); + RtasrClient client = new RtasrClient.Builder() + .signature(xfProperties.getAppId(), xfProperties.getRtasr().getApiKey()) + .lang(from) + // 设置此参数需需开通翻译功能,请在控制台申请并开通 +// .targetLang(to) + .build(); + client.send(is, app); + latch.await(); + return app.getResult(); + } catch (Exception e) { + if (e instanceof InterruptedException) { + Thread.currentThread().interrupt(); + } + logger.error("语音转写失败", e); + return AlitaResult.error("语音转写失败:{}", e.getMessage()); + } + } + + /** + * 解析转写流式响应,更新实时结果,返回此次的中间结果 + */ + public String handleAndReturnContent(String message) { + StringBuilder tempResult = new StringBuilder(); + + try { + // 解析本次服务端返回的文本内容 + + JSONObject messageObj = JSONUtil.parseObj(message); + JSONObject cn = messageObj.getJSONObject("cn"); + JSONObject st = cn.getJSONObject("st"); + JSONArray rtArr = st.getJSONArray("rt"); + for (int i = 0; i < rtArr.size(); i++) { + JSONObject rtArrObj = rtArr.getJSONObject(i); + JSONArray wsArr = rtArrObj.getJSONArray("ws"); + for (int j = 0; j < wsArr.size(); j++) { + JSONObject wsArrObj = wsArr.getJSONObject(j); + JSONArray cwArr = wsArrObj.getJSONArray("cw"); + for (int k = 0; k < cwArr.size(); k++) { + JSONObject cwArrObj = cwArr.getJSONObject(k); + String wStr = cwArrObj.getStr("w"); + tempResult.append(wStr); + } + } + } + + // 根据中间结果类型更新最终结果 + String type = st.getStr("type"); + if (StringUtils.equals("1", type)) { + // 此时服务端返回的是当前语句的实时转写内容,不保存到最终结果中,返回到调用处进行拼接展示。 + return tempResult.toString(); + } else if (StringUtils.equals("0", type)) { + // 此时服务端返回的是当前语句的完整转写内容,保存到最终结果中,并返回空字符串。 + finalResult.append(tempResult); + return ""; + } else { + logger.warn("未知的转写响应类型:{}", type); + return tempResult.toString(); + } + } catch (Exception e) { + return message; + } + } + +} diff --git a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java index 1c8dc5c..705651b 100644 --- a/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java +++ b/src/main/java/com/newyetai/sdk/xf/util/XfUtils.java @@ -1,6 +1,8 @@ package com.newyetai.sdk.xf.util; +import com.newyetai.sdk.xf.dto.RtasrReqDTO; import com.newyetai.sdk.xf.model.TranslateReq; +import com.newyetai.sdk.xf.service.RtasrClientApp; import java.io.File; import java.io.IOException; import java.io.InputStream; @@ -99,7 +101,7 @@ public class XfUtils { /** * 语音听写 - * @param filePath + * @param fileCode * @return * @author sr on 2025-01-09 */ @@ -117,7 +119,6 @@ public class XfUtils { /** * 语音合成 - * @param filePath * @return * @author sr on 2025-01-09 */ @@ -230,16 +231,42 @@ public class XfUtils { return result.getData(); } + /** + * 对话 + * @param req + * @return + */ public static String chat(ChatReq req) { AlitaResult result = XfSparkProvider.httpChat(req); Assert.isTrue(result.getSuccess(), result.getMsg()); return result.getData(); } + /** + * 翻译 + * @param req + * @return + */ public static String translate(TranslateReq req) { AlitaResult result = XfSparkProvider.translate(req); Assert.isTrue(result.getSuccess(), result.getMsg()); return result.getData(); } + /** + * 实时语音转写 + * @param reqDTO + * @return + */ + public static String rtasr(RtasrReqDTO reqDTO) { + try (InputStream is = fileRecordService.getFile(reqDTO.getFileCode())){ + AlitaResult result = RtasrClientApp.execute(is, reqDTO.getFrom(), xfProperties); + Assert.isTrue(result.getSuccess(), result.getMsg()); + return result.getData(); + } catch (Exception e) { + throw new BusinessException("语音听写异常:" + e.getMessage()); + } + + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index a927657..ad2c564 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -165,6 +165,8 @@ xf.iatHostUrl=https://iat-api.xfyun.cn/v2/iat xf.ttsHostUrl=https://tts-api.xfyun.cn/v2/tts #机器翻译niutrans xf.otsHostUrl=https://ntrans.xfyun.cn/v2/ots +#实时语音转写 +xf.rtasr.APIKey=2e33d9128a73be21b294594e5377ca53 #腾讯地图 tencent.map.key=TS7BZ-GOVCB-CUZUL-JUUAV-WTYT3-Z2B4Z diff --git a/src/main/resources/iat/ug.pcm b/src/main/resources/iat/ug.pcm new file mode 100644 index 0000000..d7a980b Binary files /dev/null and b/src/main/resources/iat/ug.pcm differ diff --git a/src/test/java/com/newyetai/SparkTest.java b/src/test/java/com/newyetai/SparkTest.java index 36d49be..7244a7d 100644 --- a/src/test/java/com/newyetai/SparkTest.java +++ b/src/test/java/com/newyetai/SparkTest.java @@ -1,5 +1,8 @@ package com.newyetai; +import com.newyetai.sdk.xf.config.XfProperties.XfAppProperties; +import com.newyetai.sdk.xf.model.TranslateReq; +import com.newyetai.sdk.xf.service.RtasrClientApp; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; @@ -47,9 +50,11 @@ public class SparkTest { static XfProperties xfProperties = XfProperties.builder() .iatHostUrl("https://iat-api.xfyun.cn/v2/iat") .ttsHostUrl("https://tts-api.xfyun.cn/v2/tts") - .appId("a2847f13") - .apiSecret("") - .apiKey("") + .otsHostUrl("https://ntrans.xfyun.cn/v2/ots") + .appId("83b8ddca") + .apiSecret("ZWE2MjkzY2VmZDkwM2EwZGJhNGRhOWJm") + .apiKey("e092f93e28ebe6d27f756bb2d884dcff") + .rtasr(XfAppProperties.builder().apiKey("2e33d9128a73be21b294594e5377ca53").build()) .build(); @Test @@ -207,7 +212,7 @@ public class SparkTest { // WebTtsWS.execute("欢迎来到讯飞开放平台", xfProperties, outputStream); // fs.write(outputStream.toByteArray()); // } - + try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); OutputStream fs = new FileOutputStream("src/main/resources/iat/323.mp3");){ AlitaResult result = WebTtsWS.execute("欢迎来到讯飞开放平台", xfProperties, outputStream); @@ -218,4 +223,20 @@ public class SparkTest { } } + @Test + public void ots(){ +// AlitaResult result = XfSparkProvider.translate(TranslateReq.builder().text("欢迎#你啊,.;@#$你好").build(), xfProperties); + AlitaResult result = XfSparkProvider.translate(TranslateReq.builder().text("تىيانەي ئەقىللىق خىزمەت مۇلازىمىتى بىرلەشمە سۇپىسىغا خوش ھەمرىيىڭىز.").from("uy").to("cn").build(), xfProperties); + log.info("{}", result); + } + + @Test + public void rtasr() throws IOException { + String file = "src/main/resources/iat/ug.pcm"; + try (InputStream is = new FileInputStream(file)) { + AlitaResult result = RtasrClientApp.execute(is, "cn_uyghur", xfProperties); + log.info("{}", result); + } + } + }