Skip to content

Commit 694bf36

Browse files
authored
feat(tool-calls): add test unit classes and provide a constant class for each tool-call (#1132)
* feat: Remove Bing Search Tool-Calling (Bing Search API will be discontinued on August 11, 2025) * feat: add AliTranslateTest * feat: Change the AutoConfiguration class for Tool-Call from non-injection by default to injection by default. * feat: add test unit class (amap, baidu map, baidu search) * feat: Provide a Constant class for each Tool-Call to store the Bean name, configuration prefix, and environment variable name of each Tool-Call. * fix: license * fix: test without api-key
1 parent 10a1f61 commit 694bf36

File tree

87 files changed

+1065
-635
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

87 files changed

+1065
-635
lines changed

community/tool-calls/README.md

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
* Properties 类命名为:`${name}Properties`,例如:`BaiduSearchProperties`
1111
* ToolFunction Impl name 命名为:`${name}Service`,通常是由声明 Bean 注解的方法名确定,如 `baiduSearchService`(建议,请根据插件实际情况确定)
1212
* ToolFunction bean name 命名为:`${name}`
13+
* Constants 类命名为:``。
14+
* 单元测试类的名称为:``。
1315

1416
2. 使用 **org.springframework.context.annotation.Description** `@Description("xxx")` 注解描述插件的功能,应提供对插件功能清晰明确的描述,例如:`@Description("百度搜索插件,用于查询百度上的新闻事件等信息")`
1517
3. 如果 Function Impl 实现较为复杂,需要使用一些自定义函数,方法命名规范为:`${name}Tool`,例如:`BaiduSearchTool`, 目录层级和实现类保持一致
@@ -34,15 +36,15 @@ public class BaiduTranslateProperties extends CommonToolCallProperties {
3436

3537
7. 在Function Impl中,JSON的序列化与反序列化统一使用`spring-ai-alibaba-starter-tool-calling-common`模块的`JsonParseTool`对象,common模块自动注入了一个默认的Bean,如果有特殊需求也可以自定义`objectMapper`,在`AutoConfiguration`中覆盖原有的`JsonParseTool`的Bean。
3638
HTTP请求统一使用common模块的`RestClientTool`或者`WebClientTool`的对象,该类有`builder`方法,必要`CommonToolCallProperties``JsonParseTool`对象,根据需要也可以自定义其他对象。
37-
8. Auto Configuration 类中,应该声明一个Function Impl的Bean,供用户使用,例如:
39+
8. Auto Configuration 类中,应该声明一个Function Impl的Bean,供用户使用,且 Bean 的名称应该在对应的 Constants 类给出,常量的名称应为`TOOL_NAME`例如:
3840

3941
```java
4042
@Configuration
4143
@EnableConfigurationProperties(BaiduTranslateProperties.class)
42-
@ConditionalOnProperty(prefix = BaiduTranslateProperties.BaiDuTranslatePrefix, name = "enabled", havingValue = "true",
44+
@ConditionalOnProperty(prefix = BaiduTranslateConstants.CONFIG_PREFIX, name = "enabled", havingValue = "true",
4345
matchIfMissing = true)
4446
public class BaiduTranslateAutoConfiguration {
45-
@Bean
47+
@Bean(name = BaiduTranslateConstants.TOOL_NAME)
4648
@ConditionalOnMissingBean
4749
@Description("Baidu translation function for general text translation")
4850
public BaiduTranslateService baiduTranslate(BaiduTranslateProperties properties, JsonParseTool jsonParseTool) {
@@ -53,5 +55,19 @@ public class BaiduTranslateAutoConfiguration {
5355
}
5456
```
5557

58+
如果该插件有多个 ToolFunction Bean,Constants 类可以分别定义每个 Bean 名称的常量,但必须以 `_TOOL_NAME` 结尾,比如`GET_ADDRESS_TOOL_NAME`
59+
5660
9. 对于多个模块可能共用的方法,应该写到common模块的`CommonToolCallUtils`类中。对于多个模块可能共用的常量,应该写到common模块的`CommonToolCallConstants`中。
57-
10. 可以参考`baidumap``baidutranslate``baidusearch`这些已经应用上述规则的代码。
61+
10. 对于 Properties 类中需要用户隐私的信息(比如 API Key),需要在 Constants 类定义一个环境变量名词,并在 Properties 类的构造方法中读取系统环境变量的值,例如:
62+
63+
```java
64+
// BaiduTranslateConstants
65+
public static final String SECRET_KEY_ENV = "BAIDU_TRANSLATE_SECRET_KEY";
66+
67+
// BaiduTranslateProperties
68+
public BaiduTranslateProperties() {
69+
this.secretKey = System.getenv(SECRET_KEY_ENV);
70+
}
71+
```
72+
73+
11. 每一个插件都需要编写单元测试类。对于需要用户隐私的信息(比如 API Key)的插件,可以在测试方法上标注 `@EnabledIfEnvironmentVariable(named = XXXConstants.API_KEY_ENV, matches = CommonToolCallConstants.NOT_BLANK_REGEX)` 注解,保证自己本地能够通过单元测试。

community/tool-calls/spring-ai-alibaba-starter-tool-calling-alitranslate/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,18 @@
6363
<version>1.0.3</version>
6464
</dependency>
6565

66+
<dependency>
67+
<groupId>org.springframework.boot</groupId>
68+
<artifactId>spring-boot-starter-test</artifactId>
69+
<scope>test</scope>
70+
</dependency>
71+
72+
<dependency>
73+
<groupId>junit</groupId>
74+
<artifactId>junit</artifactId>
75+
<scope>test</scope>
76+
</dependency>
77+
6678
</dependencies>
6779

6880
</project>

community/tool-calls/spring-ai-alibaba-starter-tool-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/toolcalling/alitranslate/AliTranslateAutoConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
@Configuration
3030
@ConditionalOnClass(AliTranslateService.class)
3131
@EnableConfigurationProperties(AliTranslateProperties.class)
32-
@ConditionalOnProperty(prefix = AliTranslateProperties.PREFIX, name = "enabled", havingValue = "true")
32+
@ConditionalOnProperty(prefix = AliTranslateConstants.CONFIG_PREFIX, name = "enabled", havingValue = "true",
33+
matchIfMissing = true)
3334
public class AliTranslateAutoConfiguration {
3435

35-
@Bean(destroyMethod = "close")
36+
@Bean(name = AliTranslateConstants.TOOL_NAME, destroyMethod = "close")
3637
@ConditionalOnMissingBean
3738
@Description("Implement natural language translation capabilities.")
3839
public AliTranslateService aliTranslateService(AliTranslateProperties properties) {
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.cloud.ai.toolcalling.alitranslate;
18+
19+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallConstants;
20+
21+
/**
22+
* @author vlsmb
23+
*/
24+
public final class AliTranslateConstants {
25+
26+
public static final String CONFIG_PREFIX = CommonToolCallConstants.TOOL_CALLING_CONFIG_PREFIX + ".alitranslate";
27+
28+
public static final String ACCESS_KEY_SECRET_ENV = "ALITRANSLATE_ACCESS_KEY_SECRET";
29+
30+
public static final String ACCESS_KEY_ID_ENV = "ALITRANSLATE_ACCESS_KEY_ID";
31+
32+
public static final String TOOL_NAME = "aliTranslateService";
33+
34+
/**
35+
* version of the api
36+
*/
37+
public static final String SCENE = "general";
38+
39+
/**
40+
* FormatType text or html
41+
*/
42+
public static final String FORM_TYPE = "text";
43+
44+
/**
45+
* offline doc:
46+
* https://help.aliyun.com/zh/machine-translation/support/supported-languages-and-codes?spm=api-workbench.api_explorer.0.0.37a94eecsclZw9
47+
*/
48+
public static final String LANGUAGE_CODE_ZH = "zh";
49+
50+
public static final String LANGUAGE_CODE_EN = "en";
51+
52+
}

community/tool-calls/spring-ai-alibaba-starter-tool-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/toolcalling/alitranslate/AliTranslateProperties.java

Lines changed: 3 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,58 +15,24 @@
1515
*/
1616
package com.alibaba.cloud.ai.toolcalling.alitranslate;
1717

18-
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallConstants;
1918
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallProperties;
2019
import org.springframework.boot.context.properties.ConfigurationProperties;
2120
import org.springframework.util.StringUtils;
2221

2322
/**
24-
* <p>
2523
* Aliyun Translation Service Configuration Attributes Class
26-
* </p>
27-
* Fields that must be configured:<br>
28-
* - Aliyun accessKeyId: Set {@link #setAccessKeyId(String)} or the environment variable
29-
* {@code ALITRANSLATE_ACCESS_KEY_ID}.<br>
30-
* - Aliyun accessKeySecret: Set {@link #setSecretKey(String)} or the environment variable
31-
* {@code ALITRANSLATE_ACCESS_KEY_SECRET}.<br>
3224
*
33-
* @author yunlong
3425
* @author Allen Hu
3526
*/
36-
@ConfigurationProperties(prefix = AliTranslateProperties.PREFIX)
27+
@ConfigurationProperties(prefix = AliTranslateConstants.CONFIG_PREFIX)
3728
public class AliTranslateProperties extends CommonToolCallProperties {
3829

39-
public static final String PREFIX = CommonToolCallConstants.TOOL_CALLING_CONFIG_PREFIX + ".alitranslate";
40-
41-
private String region;
42-
4330
public AliTranslateProperties() {
44-
this.setPropertiesFromEnv(null, "ALITRANSLATE_ACCESS_KEY_SECRET", null, null);
45-
String accessKeyIdEnv = "ALITRANSLATE_ACCESS_KEY_ID";
31+
this.setPropertiesFromEnv(null, AliTranslateConstants.ACCESS_KEY_SECRET_ENV, null, null);
32+
String accessKeyIdEnv = AliTranslateConstants.ACCESS_KEY_ID_ENV;
4633
if (!StringUtils.hasText(this.getAccessKeyId())) {
4734
this.setAccessKeyId(System.getenv(accessKeyIdEnv));
4835
}
4936
}
5037

51-
public String getAccessKeySecret() {
52-
return getSecretKey();
53-
}
54-
55-
/**
56-
* @param accessKeySecret AccessKeySecret
57-
* @deprecated use {@link #setSecretKey(String)} instead
58-
*/
59-
@Deprecated
60-
public void setAccessKeySecret(String accessKeySecret) {
61-
this.setSecretKey(accessKeySecret);
62-
}
63-
64-
public String getRegion() {
65-
return region;
66-
}
67-
68-
public void setRegion(String region) {
69-
this.region = region;
70-
}
71-
7238
}

community/tool-calls/spring-ai-alibaba-starter-tool-calling-alitranslate/src/main/java/com/alibaba/cloud/ai/toolcalling/alitranslate/AliTranslateService.java

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -45,35 +45,13 @@ public class AliTranslateService
4545

4646
private final AsyncClient client;
4747

48-
/**
49-
* version of the api
50-
*/
51-
public static final String SCENE = "general";
52-
53-
/**
54-
* FormatType text or html
55-
*/
56-
public static final String FORM_TYPE = "text";
57-
58-
/**
59-
* offline doc:
60-
* https://help.aliyun.com/zh/machine-translation/support/supported-languages-and-codes?spm=api-workbench.api_explorer.0.0.37a94eecsclZw9
61-
*/
62-
public static final String LANGUAGE_CODE_ZH = "zh";
63-
64-
public static final String LANGUAGE_CODE_EN = "en";
65-
6648
public AliTranslateService(AliTranslateProperties properties) {
67-
assert StringUtils.hasText(properties.getRegion());
68-
assert StringUtils.hasText(properties.getAccessKeyId());
69-
assert StringUtils.hasText(properties.getAccessKeySecret());
7049
StaticCredentialProvider provider = StaticCredentialProvider.create(Credential.builder()
7150
.accessKeyId(properties.getAccessKeyId())
72-
.accessKeySecret(properties.getAccessKeySecret())
51+
.accessKeySecret(properties.getSecretKey())
7352
.build());
7453

7554
this.client = AsyncClient.builder()
76-
.region(properties.getRegion()) // Region ID
7755
.credentialsProvider(provider)
7856
.overrideConfiguration(ClientOverrideConfiguration.create().setEndpointOverride("mt.aliyuncs.com"))
7957
.build();
@@ -87,11 +65,11 @@ public Response apply(Request request) {
8765
}
8866

8967
TranslateGeneralRequest translateGeneralRequest = TranslateGeneralRequest.builder()
90-
.formatType(FORM_TYPE)
68+
.formatType(AliTranslateConstants.FORM_TYPE)
9169
.sourceLanguage(request.sourceLanguage)
9270
.targetLanguage(request.targetLanguage)
9371
.sourceText(request.text)
94-
.scene(SCENE)
72+
.scene(AliTranslateConstants.SCENE)
9573
.build();
9674

9775
CompletableFuture<TranslateGeneralResponse> response = client.translateGeneral(translateGeneralRequest);
@@ -145,7 +123,7 @@ public record Request(
145123
value = "targetLanguage") @JsonPropertyDescription("Target language to alitranslate into, default is en") String targetLanguage) {
146124

147125
public Request(@JsonProperty("text") String text) {
148-
this(text, LANGUAGE_CODE_ZH, LANGUAGE_CODE_EN);
126+
this(text, AliTranslateConstants.LANGUAGE_CODE_ZH, AliTranslateConstants.LANGUAGE_CODE_EN);
149127
}
150128
}
151129

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.alibaba.cloud.ai.toolcalling.alitranslate;
18+
19+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallAutoConfiguration;
20+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallConstants;
21+
import org.junit.jupiter.api.DisplayName;
22+
import org.junit.jupiter.api.Test;
23+
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
24+
import org.slf4j.Logger;
25+
import org.slf4j.LoggerFactory;
26+
import org.springframework.beans.factory.annotation.Autowired;
27+
import org.springframework.boot.test.context.SpringBootTest;
28+
import org.springframework.util.StringUtils;
29+
30+
/**
31+
* @author vlsmb
32+
*/
33+
@SpringBootTest(classes = { AliTranslateAutoConfiguration.class, CommonToolCallAutoConfiguration.class })
34+
@DisplayName("ALiTranslate Service Test")
35+
public class AliTranslateTest {
36+
37+
@Autowired
38+
private AliTranslateService aliTranslateService;
39+
40+
private static final Logger log = LoggerFactory.getLogger(AliTranslateTest.class);
41+
42+
@Test
43+
@DisplayName("Tool-Calling Test")
44+
@EnabledIfEnvironmentVariable(named = AliTranslateConstants.ACCESS_KEY_SECRET_ENV,
45+
matches = CommonToolCallConstants.NOT_BLANK_REGEX)
46+
@EnabledIfEnvironmentVariable(named = AliTranslateConstants.ACCESS_KEY_ID_ENV,
47+
matches = CommonToolCallConstants.NOT_BLANK_REGEX)
48+
public void testAliTranslate() {
49+
AliTranslateService.Response resp = aliTranslateService.apply(new AliTranslateService.Request("你好"));
50+
log.info("Ali Translate Service Response: {}", resp);
51+
assert resp != null && StringUtils.hasText(resp.translatedTexts());
52+
}
53+
54+
}

community/tool-calls/spring-ai-alibaba-starter-tool-calling-amap/pom.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@
5858
<version>${project.version}</version>
5959
</dependency>
6060

61+
<dependency>
62+
<groupId>org.springframework.boot</groupId>
63+
<artifactId>spring-boot-starter-test</artifactId>
64+
<scope>test</scope>
65+
</dependency>
66+
67+
<dependency>
68+
<groupId>junit</groupId>
69+
<artifactId>junit</artifactId>
70+
<scope>test</scope>
71+
</dependency>
72+
6173
</dependencies>
6274

6375
</project>

community/tool-calls/spring-ai-alibaba-starter-tool-calling-amap/src/main/java/com/alibaba/cloud/ai/toolcalling/amp/AmapAutoConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,11 @@
2929
*/
3030
@Configuration
3131
@EnableConfigurationProperties(AmapProperties.class)
32-
@ConditionalOnProperty(prefix = AmapProperties.AMAP_PREFIX, name = "enabled", havingValue = "true")
32+
@ConditionalOnProperty(prefix = AmapConstants.CONFIG_PREFIX, name = "enabled", havingValue = "true",
33+
matchIfMissing = true)
3334
public class AmapAutoConfiguration {
3435

35-
@Bean
36+
@Bean(name = AmapConstants.TOOL_NAME)
3637
@ConditionalOnMissingBean
3738
@Description("Get weather information according to address from Amap.")
3839
public WeatherSearchService gaoDeGetAddressWeather(JsonParseTool jsonParseTool, AmapProperties amapProperties) {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*
2+
* Copyright 2024-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.alibaba.cloud.ai.toolcalling.amp;
17+
18+
import static com.alibaba.cloud.ai.toolcalling.common.CommonToolCallConstants.TOOL_CALLING_CONFIG_PREFIX;
19+
20+
/**
21+
* @author vlsmb
22+
*/
23+
public final class AmapConstants {
24+
25+
public static final String CONFIG_PREFIX = TOOL_CALLING_CONFIG_PREFIX + ".amap";
26+
27+
public static final String API_KEY_ENV = "GAODE_AMAP_API_KEY";
28+
29+
public static final String TOOL_NAME = "gaoDeGetAddressWeather";
30+
31+
}

0 commit comments

Comments
 (0)