Skip to content

Commit 0776691

Browse files
authored
Merge branch 'main' into feat-streamhttpnode
2 parents f1659ae + db4b071 commit 0776691

File tree

67 files changed

+3928
-583
lines changed

Some content is hidden

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

67 files changed

+3928
-583
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# 百度搜索使用说明
2+
3+
## 普通搜索
4+
5+
普通搜索可以直接使用 BaiduSearchService.query 或 BaiduSearchService.apply 方法进行搜索
6+
可以使用 spring.ai.alibaba.toolcalling.baidu.search.maxResults 添加默认的最大条数
7+
8+
## 百度千帆智能搜索
9+
10+
使用百度千帆智能搜索需要添加如下配置
11+
spring.ai.alibaba.toolcalling.baidu.search.ai.api-key=<your key>
12+
spring.ai.alibaba.toolcalling.baidu.search.ai.enabled=true
13+
14+
其中 api-key 也可以通过环境变量 BAIDU_API_KEY 进行设置
15+
16+
然后使用 BaiduAiSearchService.query 或 BaiduAiSearchService.apply 方法进行搜索
17+
18+
参考文档 https://cloud.baidu.com/doc/AppBuilder/s/pmaxd1hvy
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Baidu Search Usage Instructions
2+
3+
## Regular Search
4+
5+
Regular search can be performed directly using the `BaiduSearchService.query` or `BaiduSearchService.apply` methods.
6+
You can use `spring.ai.alibaba.toolcalling.baidu.search.maxResults` to add a default maximum number of results.
7+
8+
## Baidu Qianfan AI Search
9+
10+
To use Baidu Qianfan AI Search, add the following configuration:
11+
```
12+
spring.ai.alibaba.toolcalling.baidu.search.ai.api-key=<your key>
13+
spring.ai.alibaba.toolcalling.baidu.search.ai.enabled=true
14+
```
15+
16+
The api-key can also be set through the environment variable `BAIDU_API_KEY`.
17+
18+
Then use the `BaiduAiSearchService.query` or `BaiduAiSearchService.apply` methods to perform searches.
19+
20+
Reference documentation: https://cloud.baidu.com/doc/AppBuilder/s/pmaxd1hvy
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.baidusearch;
17+
18+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallProperties;
19+
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
21+
/**
22+
* @author HunterPorter
23+
*/
24+
@ConfigurationProperties(prefix = BaiduSearchConstants.CONFIG_PREFIX_AI)
25+
public class BaiduAiSearchProperties extends CommonToolCallProperties {
26+
27+
public BaiduAiSearchProperties() {
28+
super("https://qianfan.baidubce.com");
29+
this.setPropertiesFromEnv(BaiduSearchConstants.API_KEY_ENV, null, null, null);
30+
}
31+
32+
}
Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
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.baidusearch;
17+
18+
import com.alibaba.cloud.ai.toolcalling.common.JsonParseTool;
19+
import com.alibaba.cloud.ai.toolcalling.common.WebClientTool;
20+
import com.alibaba.cloud.ai.toolcalling.common.interfaces.SearchService;
21+
import com.fasterxml.jackson.annotation.JsonClassDescription;
22+
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
23+
import com.fasterxml.jackson.annotation.JsonInclude;
24+
import com.fasterxml.jackson.annotation.JsonProperty;
25+
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
import org.springframework.util.StringUtils;
29+
30+
import java.util.List;
31+
import java.util.function.Function;
32+
33+
/**
34+
* baidu AI Search
35+
* <a href="https://cloud.baidu.com/doc/AppBuilder/s/pmaxd1hvy">qianfan</a> This version
36+
* is relatively stable, but requires apiKey configuration, with 100 free queries per day
37+
*
38+
* @author HunterPorter
39+
* @author <a href="mailto:zongpeng_hzp@163.com">HunterPorter</a>
40+
*/
41+
public class BaiduAiSearchService
42+
implements SearchService, Function<BaiduAiSearchService.Request, BaiduAiSearchService.Response> {
43+
44+
private static final Logger log = LoggerFactory.getLogger(BaiduAiSearchService.class);
45+
46+
private final WebClientTool webClientTool;
47+
48+
private final JsonParseTool jsonParseTool;
49+
50+
private final BaiduAiSearchProperties properties;
51+
52+
public BaiduAiSearchService(WebClientTool webClientTool, JsonParseTool jsonParseTool,
53+
BaiduAiSearchProperties properties) {
54+
this.webClientTool = webClientTool;
55+
this.jsonParseTool = jsonParseTool;
56+
this.properties = properties;
57+
}
58+
59+
@Override
60+
public Response query(String query) {
61+
return this.apply(Request.simplyQuery(query));
62+
}
63+
64+
@Override
65+
public Response apply(Request request) {
66+
if (!StringUtils.hasText(properties.getApiKey())) {
67+
throw new RuntimeException("Service Api Key is Invalid.");
68+
}
69+
try {
70+
String responseStr = webClientTool.post("/v2/ai_search/chat/completions", request).block();
71+
log.debug("Response: {}", responseStr);
72+
return jsonParseTool.jsonToObject(responseStr, Response.class);
73+
}
74+
catch (Exception e) {
75+
log.error("Service Baidu AI Request Error: ", e);
76+
throw new RuntimeException(e);
77+
}
78+
}
79+
80+
@JsonClassDescription("Return real-time search results from web and other data sources based on questions or keywords")
81+
@JsonInclude(JsonInclude.Include.NON_NULL)
82+
public record Request(@JsonProperty(required = true,
83+
value = "messages") @JsonPropertyDescription("Search input, including user query content") List<Message> messages,
84+
@JsonProperty(value = "edition",
85+
defaultValue = "standard") @JsonPropertyDescription("""
86+
Search version. Default is standard.
87+
Optional values:
88+
standard: Full version.
89+
lite: Standard version, a simplified version of the full version with better latency performance, but slightly weaker effect.""") String edition,
90+
@JsonProperty(value = "search_source",
91+
defaultValue = "baidu_search_v2") @JsonPropertyDescription("Search engine version used; fixed value: baidu_search_v2") String searchSource,
92+
@JsonProperty(
93+
value = "resource_type_filter") @JsonPropertyDescription("""
94+
Support setting web, video, image, and Aladdin search modalities. The maximum value of top_k for web is 50, for video is 10, for image is 30, and for Aladdin is 5. The default value is:
95+
[{"type": "web","top_k": 20},{"type": "video","top_k": 0},{"type": "image","top_k": 0},{"type": "aladdin","top_k": 0}]
96+
Notes when using Aladdin:
97+
1. Aladdin does not support site and timeliness filtering.
98+
2. It is recommended to use it with web modality to increase the number of search returns.
99+
3. Aladdin's return parameters are in beta version and may change in the future.""") List<SearchResource> resourceTypeFilter,
100+
@JsonProperty(
101+
value = "search_filter") @JsonPropertyDescription("Filter retrieval based on conditions") SearchFilter searchFilter,
102+
@JsonProperty(
103+
value = "block_websites") @JsonPropertyDescription("List of sites to be blocked") List<String> blockWebsites,
104+
@JsonProperty(value = "search_recency_filter") @JsonPropertyDescription("""
105+
Filter by web page publication time.
106+
Enumeration values:
107+
week: Last 7 days
108+
month: Last 30 days
109+
semiyear: Last 180 days
110+
year: Last 365 days""") String searchRecencyFilter) implements SearchService.Request {
111+
112+
@Override
113+
public String getQuery() {
114+
if (messages != null && !messages.isEmpty()) {
115+
Message lastMessage = messages.get(messages.size() - 1);
116+
if ("user".equals(lastMessage.role)) {
117+
return lastMessage.content;
118+
}
119+
}
120+
return null;
121+
}
122+
123+
public static Request simplyQuery(String query) {
124+
return new Request(List.of(new Message("user", query)), "standard", "baidu_search_v2",
125+
List.of(new SearchResource("web", 20)), null, null, null);
126+
}
127+
}
128+
129+
public record Message(
130+
@JsonProperty("role") @JsonPropertyDescription("Role setting, optional values: user: user; assistant: model") String role,
131+
@JsonProperty("content") @JsonPropertyDescription("""
132+
When content is text, it corresponds to the dialog content, that is, the user's query question. Notes:
133+
1. Cannot be empty.
134+
2. In multi-round conversations, the last user input content cannot be empty characters, such as spaces, "\\n", "\\r", "\\f", etc.""") String content) {
135+
}
136+
137+
public record SearchResource(@JsonProperty("type") @JsonPropertyDescription("""
138+
Search resource type. Optional values:
139+
web: Web page
140+
video: Video
141+
image: Image
142+
aladdin: Aladdin""") String type,
143+
@JsonProperty("top_k") @JsonPropertyDescription("Specify the maximum number of returns for the modality.") Integer topK) {
144+
}
145+
146+
public record SearchFilter(@JsonProperty("match") @JsonPropertyDescription("Site condition query") Match match,
147+
@JsonProperty("range") @JsonPropertyDescription("Time range query") Range range) {
148+
}
149+
150+
public record Match(
151+
@JsonProperty("site") @JsonPropertyDescription("Support setting search conditions for specified sites, that is, content search only in the set sites. Currently supports setting 20 sites. Example: [\"tieba.baidu.com\"]") List<String> site) {
152+
}
153+
154+
public record Range(@JsonProperty("page_time") PageTime pageTime) {
155+
}
156+
157+
public record PageTime(
158+
@JsonProperty("gte") @JsonPropertyDescription("Time query parameter, greater than or equal to. Supported time units: y (year), M (month), w (week), d (day), for example \"now-1w/d\", one week ago, rounded down") String gte,
159+
@JsonProperty("gt") @JsonPropertyDescription("Time query parameter, greater than. Supported time units: y (year), M (month), w (week), d (day), for example \"now-1w/d\", one week ago, rounded up") String gt,
160+
@JsonProperty("lte") @JsonPropertyDescription("Time query parameter, less than or equal to. Supported time units: y (year), M (month), w (week), d (day), for example \"now-1w/d\", one week ago, rounded up") String lte,
161+
@JsonProperty("lt") @JsonPropertyDescription("Time query parameter, less than. Supported time units: y (year), M (month), w (week), d (day), for example \"now-1w/d\", one week ago, rounded down") String lt) {
162+
}
163+
164+
@JsonClassDescription("Baidu AI Search Response")
165+
@JsonIgnoreProperties(ignoreUnknown = true)
166+
public record Response(@JsonProperty("requestId") @JsonPropertyDescription("Request ID") String requestId,
167+
@JsonProperty("code") @JsonPropertyDescription("Error code, returned when an exception occurs") String code,
168+
@JsonProperty("message") @JsonPropertyDescription("Error message, returned when an exception occurs") String message,
169+
@JsonProperty("references") @JsonPropertyDescription("Search result list") List<Reference> references)
170+
implements
171+
SearchService.Response {
172+
173+
@Override
174+
public SearchResult getSearchResult() {
175+
if (references == null || references.isEmpty()) {
176+
return new SearchResult(List.of());
177+
}
178+
179+
return new SearchResult(this.references()
180+
.stream()
181+
.map(item -> new SearchContent(item.title(), item.content(), item.url(), null))
182+
.toList());
183+
}
184+
185+
public record Reference(@JsonProperty("icon") @JsonPropertyDescription("Website icon address") String icon,
186+
@JsonProperty("id") @JsonPropertyDescription("Reference number") Integer id,
187+
@JsonProperty("title") @JsonPropertyDescription("Title") String title,
188+
@JsonProperty("url") @JsonPropertyDescription("URL") String url,
189+
@JsonProperty("web_anchor") @JsonPropertyDescription("Anchor") String webAnchor,
190+
@JsonProperty("website") @JsonPropertyDescription("Website name") String website,
191+
@JsonProperty("content") @JsonPropertyDescription("Content") String content,
192+
@JsonProperty("date") @JsonPropertyDescription("Date") String date,
193+
@JsonProperty("type") @JsonPropertyDescription("""
194+
Retrieval resource type. Return values:
195+
web: Web page
196+
video: Video content
197+
image: Image
198+
aladdin: Aladdin""") String type,
199+
@JsonProperty("image") @JsonPropertyDescription("Image information") ImageDetail image,
200+
@JsonProperty("video") @JsonPropertyDescription("Video information") VideoDetail video,
201+
@JsonProperty("is_aladdin") @JsonPropertyDescription("Whether it is Aladdin content") Boolean isAladdin,
202+
@JsonProperty("aladdin") @JsonPropertyDescription("Aladdin content") Object aladdin) {
203+
}
204+
205+
public record ImageDetail(String url, String height, String width) {
206+
}
207+
208+
public record VideoDetail(String url, String height, String width, String size, String duration,
209+
String hoverPic) {
210+
}
211+
}
212+
213+
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
* @author KrakenZJC
3535
**/
3636
@Configuration
37-
@EnableConfigurationProperties(BaiduSearchProperties.class)
37+
@EnableConfigurationProperties({ BaiduSearchProperties.class, BaiduAiSearchProperties.class })
3838
@ConditionalOnProperty(prefix = BaiduSearchConstants.CONFIG_PREFIX, name = "enabled", havingValue = "true",
3939
matchIfMissing = true)
4040
public class BaiduSearchAutoConfiguration {
@@ -54,4 +54,18 @@ public BaiduSearchService baiduSearch(JsonParseTool jsonParseTool, BaiduSearchPr
5454
WebClientTool.builder(jsonParseTool, properties).httpHeadersConsumer(consumer).build());
5555
}
5656

57+
@Bean(name = BaiduSearchConstants.TOOL_NAME_AI)
58+
@ConditionalOnMissingBean
59+
@Description("Use baidu ai search engine to query information.")
60+
@ConditionalOnProperty(prefix = BaiduSearchConstants.CONFIG_PREFIX_AI, name = "enabled", havingValue = "true")
61+
public BaiduAiSearchService baiduAiSearch(JsonParseTool jsonParseTool, BaiduAiSearchProperties properties) {
62+
Consumer<HttpHeaders> consumer = headers -> {
63+
headers.add("Content-Type", "application/json");
64+
headers.add("Authorization", "Bearer " + properties.getApiKey());
65+
};
66+
return new BaiduAiSearchService(
67+
WebClientTool.builder(jsonParseTool, properties).httpHeadersConsumer(consumer).build(), jsonParseTool,
68+
properties);
69+
}
70+
5771
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public final class BaiduSearchConstants {
2424

2525
public static final String CONFIG_PREFIX = TOOL_CALLING_CONFIG_PREFIX + ".baidu.search";
2626

27+
public static final String CONFIG_PREFIX_AI = TOOL_CALLING_CONFIG_PREFIX + ".baidu.search.ai";
28+
2729
public static final String TOOL_NAME = "baiduSearch";
2830

31+
public static final String TOOL_NAME_AI = "baiduAiSearch";
32+
33+
public static final String API_KEY_ENV = "BAIDU_API_KEY";
34+
2935
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
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.baidusearch;
17+
18+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallAutoConfiguration;
19+
import com.alibaba.cloud.ai.toolcalling.common.CommonToolCallConstants;
20+
import com.alibaba.cloud.ai.toolcalling.common.interfaces.SearchService;
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.springframework.beans.factory.annotation.Autowired;
25+
import org.springframework.beans.factory.annotation.Qualifier;
26+
import org.springframework.boot.test.context.SpringBootTest;
27+
28+
import java.util.logging.Logger;
29+
30+
@SpringBootTest(classes = { CommonToolCallAutoConfiguration.class, BaiduSearchAutoConfiguration.class },
31+
properties = "spring.ai.alibaba.toolcalling.baidu.search.ai.enabled=true")
32+
@DisplayName("Baidu AI Search Test")
33+
@EnabledIfEnvironmentVariable(named = BaiduSearchConstants.API_KEY_ENV,
34+
matches = CommonToolCallConstants.NOT_BLANK_REGEX)
35+
public class BaiduAiSearchTest {
36+
37+
@Autowired
38+
private BaiduAiSearchService baiduAiSearchService;
39+
40+
private static final Logger log = Logger.getLogger(BaiduAiSearchTest.class.getName());
41+
42+
@Test
43+
@DisplayName("Tool-Calling Test")
44+
public void testBaiduSearch() {
45+
BaiduAiSearchService.Request request = BaiduAiSearchService.Request.simplyQuery("Spring AI Alibaba");
46+
var resp = baiduAiSearchService.apply(request);
47+
assert resp != null && resp.references() != null;
48+
log.info("results: " + resp.references());
49+
}
50+
51+
@Autowired
52+
@Qualifier("baiduAiSearch")
53+
private SearchService searchService;
54+
55+
@Test
56+
@DisplayName("Abstract Search Service Test")
57+
public void testAbstractSearch() {
58+
var resp = searchService.query("Spring AI Alibaba");
59+
assert resp != null && resp.getSearchResult() != null && resp.getSearchResult().results() != null
60+
&& !resp.getSearchResult().results().isEmpty();
61+
log.info("results: " + resp.getSearchResult());
62+
}
63+
64+
}

0 commit comments

Comments
 (0)