From 6cda03ea4ae2b331337bac208eb14389d3ce3b41 Mon Sep 17 00:00:00 2001
From: Yeaury <3144550951@qq.com>
Date: Wed, 25 Dec 2024 17:18:24 +0800
Subject: [PATCH 1/3] add youdao translate function
---
.../pom.xml | 61 +++++++++
.../youdaotranslate/AuthUtil.java | 59 +++++++++
.../YoudaoTranslateAutoConfiguration.java | 42 ++++++
.../YoudaoTranslateProperties.java | 46 +++++++
.../YoudaoTranslateService.java | 123 ++++++++++++++++++
...ot.autoconfigure.AutoConfiguration.imports | 1 +
pom.xml | 1 +
7 files changed, 333 insertions(+)
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/pom.xml
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/AuthUtil.java
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateAutoConfiguration.java
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateProperties.java
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateService.java
create mode 100644 community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/pom.xml b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/pom.xml
new file mode 100644
index 00000000..b51d1922
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/pom.xml
@@ -0,0 +1,61 @@
+
+
+
+
+
+ 4.0.0
+
+
+ com.alibaba.cloud.ai
+ spring-ai-alibaba
+ ${revision}
+ ../../../pom.xml
+
+
+ spring-ai-alibaba-starter-function-calling-youdaotranslate
+ spring-ai-alibaba-starter-function-calling-youdao
+ youdao translate tool for Spring AI Alibaba
+
+
+
+ org.springframework.ai
+ spring-ai-spring-boot-autoconfigure
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.springframework.boot
+ spring-boot-starter-webflux
+
+
+
+ com.google.code.gson
+ gson
+
+
+
+
+
+ spring-ai-alibaba-function-calling-translate
+
+
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/AuthUtil.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/AuthUtil.java
new file mode 100644
index 00000000..b91f7f32
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/AuthUtil.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.youdaotranslate;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * @author Yeaury
+ */
+public class AuthUtil {
+
+ public static String calculateSign(String appKey, String appSecret, String q, String salt, String curtime)
+ throws NoSuchAlgorithmException {
+ String strSrc = appKey + getInput(q) + salt + curtime + appSecret;
+ return sha256(strSrc);
+ }
+
+ private static String sha256(String strSrc) throws NoSuchAlgorithmException {
+ byte[] bt = strSrc.getBytes();
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ md.update(bt);
+ byte[] bts = md.digest();
+ StringBuilder des = new StringBuilder();
+ for (byte b : bts) {
+ String tmp = (Integer.toHexString(b & 0xFF));
+ if (tmp.length() == 1) {
+ des.append("0");
+ }
+ des.append(tmp);
+ }
+ return des.toString();
+ }
+
+ private static String getInput(String q) {
+ if (q.length() > 20) {
+ String firstPart = q.substring(0, 10);
+ String lastPart = q.substring(q.length() - 10);
+ return firstPart + q.length() + lastPart;
+ }
+ else {
+ return q;
+ }
+ }
+
+}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateAutoConfiguration.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateAutoConfiguration.java
new file mode 100644
index 00000000..3e6c57e2
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateAutoConfiguration.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.youdaotranslate;
+
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Description;
+
+/**
+ * @author Yeaury
+ */
+@Configuration
+@ConditionalOnClass(YoudaoTranslateService.class)
+@EnableConfigurationProperties(YoudaoTranslateProperties.class)
+@ConditionalOnProperty(prefix = "spring.ai.alibaba.functioncalling.youdao", name = "enabled", havingValue = "true")
+public class YoudaoTranslateAutoConfiguration {
+
+ @Bean
+ @ConditionalOnMissingBean
+ @Description("use youdao translation to achieve translation")
+ public YoudaoTranslateService youdaoTranslateFunction(YoudaoTranslateProperties properties) {
+ return new YoudaoTranslateService(properties);
+ }
+
+}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateProperties.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateProperties.java
new file mode 100644
index 00000000..264faa7a
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateProperties.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.youdaotranslate;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+/**
+ * @author Yeaury
+ */
+@ConfigurationProperties(prefix = "spring.ai.alibaba.functioncalling.youdao")
+public class YoudaoTranslateProperties {
+
+ private String appKey;
+
+ private String appSecret;
+
+ public String getAppKey() {
+ return appKey;
+ }
+
+ public void setAppKey(String appKey) {
+ this.appKey = appKey;
+ }
+
+ public String getAppSecret() {
+ return appSecret;
+ }
+
+ public void setAppSecret(String appSecret) {
+ this.appSecret = appSecret;
+ }
+
+}
diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateService.java b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateService.java
new file mode 100644
index 00000000..e09eb168
--- /dev/null
+++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/java/com/alibaba/cloud/ai/functioncalling/youdaotranslate/YoudaoTranslateService.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2024-2025 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.alibaba.cloud.ai.functioncalling.youdaotranslate;
+
+import com.fasterxml.jackson.annotation.JsonClassDescription;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonPropertyDescription;
+import com.google.gson.Gson;
+import com.google.gson.reflect.TypeToken;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.MediaType;
+import org.springframework.util.StringUtils;
+import org.springframework.web.reactive.function.client.WebClient;
+import org.springframework.web.util.UriComponentsBuilder;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+import java.util.function.Function;
+
+import static com.alibaba.cloud.ai.functioncalling.youdaotranslate.AuthUtil.calculateSign;
+
+/**
+ * @author Yeaury
+ */
+public class YoudaoTranslateService
+ implements Function {
+
+ private static final Logger logger = LoggerFactory.getLogger(YoudaoTranslateService.class);
+
+ private static final String YOUDAO_TRANSLATE_HOST_URL = "https://openapi.youdao.com/api";
+
+ private final String appKey;
+
+ private final String appSecret;
+
+ private final WebClient webClient;
+
+ public YoudaoTranslateService(YoudaoTranslateProperties properties) {
+ this.appKey = properties.getAppKey();
+ this.appSecret = properties.getAppSecret();
+ this.webClient = WebClient.builder()
+ .defaultHeader(HttpHeaders.USER_AGENT, HttpHeaders.USER_AGENT)
+ .defaultHeader(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)
+ .defaultHeader(HttpHeaders.ACCEPT_ENCODING, "gzip, deflate")
+ .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/json")
+ .defaultHeader(HttpHeaders.ACCEPT_LANGUAGE, "zh-CN,zh;q=0.9,ja;q=0.8")
+ .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(5 * 1024 * 1024))
+ .build();
+ }
+
+ @Override
+ public Response apply(Request request) {
+ if (request == null || !StringUtils.hasText(request.text) || !StringUtils.hasText(request.targetLanguage)) {
+ return null;
+ }
+ String curtime = String.valueOf(System.currentTimeMillis() / 1000);
+ String salt = UUID.randomUUID().toString();
+ try {
+ String url = UriComponentsBuilder.fromHttpUrl(YOUDAO_TRANSLATE_HOST_URL)
+ .queryParam("q", request.text)
+ .queryParam("from", request.sourceLanguage)
+ .queryParam("to", request.targetLanguage)
+ .queryParam("appKey", appKey)
+ .queryParam("salt", salt)
+ .queryParam("sign", calculateSign(appKey, appSecret, request.text, salt, curtime))
+ .queryParam("signType", "v3")
+ .queryParam("curtime", curtime)
+ .build()
+ .toUriString();
+
+ Mono response = webClient.get().uri(url).retrieve().bodyToMono(String.class);
+ String responseData = response.block();
+ System.out.println(responseData);
+ assert responseData != null;
+ logger.info("Translation request: {}, response: {}", request.text, responseData);
+ Gson gson = new Gson();
+ Map responseMap = gson.fromJson(responseData, new TypeToken