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 000000000..fdd08393a --- /dev/null +++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/pom.xml @@ -0,0 +1,62 @@ + + + + + + 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-youdaotranslate + 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-youdaotranslate + + 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 000000000..b91f7f323 --- /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 000000000..0858b4e4c --- /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,43 @@ +/* + * 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.youdaotranslate", 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 000000000..974c45dbc --- /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.youdaotranslate") +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 000000000..e09eb1687 --- /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>() { + }.getType()); + if (responseMap.containsKey("translation")) { + return new Response((List) responseMap.get("translation")); + } + } + catch (Exception e) { + logger.error("Failed to invoke Youdao translate API due to: {}", e.getMessage()); + return null; + } + return null; + } + + @JsonClassDescription("Request to translate text to a target language") + public record Request( + @JsonProperty(required = true, + value = "text") @JsonPropertyDescription("Content that needs to be translated") String text, + + @JsonProperty(required = true, + value = "sourceLanguage") @JsonPropertyDescription("Source language") String sourceLanguage, + + @JsonProperty(required = true, + value = "targetLanguage") @JsonPropertyDescription("Target language to translate into") String targetLanguage) { + } + + @JsonClassDescription("Response to translate text to a target language") + public record Response(List translatedTexts) { + } + +} diff --git a/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports new file mode 100644 index 000000000..bb52e1c00 --- /dev/null +++ b/community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports @@ -0,0 +1 @@ +com.alibaba.cloud.ai.functioncalling.youdaotranslate.YoudaoTranslateAutoConfiguration diff --git a/pom.xml b/pom.xml index d27918204..8c75148fa 100644 --- a/pom.xml +++ b/pom.xml @@ -54,6 +54,7 @@ community/function-calling/spring-ai-alibaba-starter-function-calling-yuque community/function-calling/spring-ai-alibaba-starter-function-calling-googletranslate community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate + community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate community/document-readers/github-document-reader community/document-readers/poi-document-reader