Skip to content

Commit

Permalink
Merge pull request #315 from Yeaury/main
Browse files Browse the repository at this point in the history
feat: add youdao translate function
  • Loading branch information
chickenlj authored Jan 13, 2025
2 parents 45ce298 + 3c62e92 commit 24e9d13
Show file tree
Hide file tree
Showing 7 changed files with 335 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>

<!--
~ 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<parent>
<groupId>com.alibaba.cloud.ai</groupId>
<artifactId>spring-ai-alibaba</artifactId>
<version>${revision}</version>
<relativePath>../../../pom.xml</relativePath>
</parent>

<artifactId>spring-ai-alibaba-starter-function-calling-youdaotranslate</artifactId>
<name>spring-ai-alibaba-starter-function-calling-youdaotranslate</name>
<description>youdao translate tool for Spring AI Alibaba</description>

<dependencies>

<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-spring-boot-autoconfigure</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>

</dependencies>

<build>
<finalName>spring-ai-alibaba-function-calling-youdaotranslate</finalName>
</build>
</project>
Original file line number Diff line number Diff line change
@@ -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;
}
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}
Original file line number Diff line number Diff line change
@@ -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;
}

}
Original file line number Diff line number Diff line change
@@ -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<YoudaoTranslateService.Request, YoudaoTranslateService.Response> {

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<String> 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<String, Object> responseMap = gson.fromJson(responseData, new TypeToken<Map<String, Object>>() {
}.getType());
if (responseMap.containsKey("translation")) {
return new Response((List<String>) 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<String> translatedTexts) {
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
com.alibaba.cloud.ai.functioncalling.youdaotranslate.YoudaoTranslateAutoConfiguration
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
<module>community/function-calling/spring-ai-alibaba-starter-function-calling-kuaidi100</module>
<module>community/function-calling/spring-ai-alibaba-starter-function-calling-googletranslate</module>
<module>community/function-calling/spring-ai-alibaba-starter-function-calling-alitranslate</module>
<module>community/function-calling/spring-ai-alibaba-starter-function-calling-youdaotranslate</module>

<module>community/document-readers/github-document-reader</module>
<module>community/document-readers/poi-document-reader</module>
Expand Down

0 comments on commit 24e9d13

Please # to comment.