diff --git a/docker/.env.example b/docker/.env.example
index 8efd57cb..9b73a290 100644
--- a/docker/.env.example
+++ b/docker/.env.example
@@ -378,6 +378,9 @@ GID='1000'
#------ Bing Search ----------- https://portal.azure.com/
# AGENT_BING_SEARCH_API_KEY=
+#------ Baidu Search ----------- https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5
+# AGENT_BAIDU_SEARCH_API_KEY=
+
#------ Serply.io ----------- https://serply.io/
# AGENT_SERPLY_API_KEY=
@@ -448,4 +451,4 @@ GID='1000'
# (optional) Comma-separated list of skills that are auto-approved.
# This will allow the skill to be invoked without user interaction.
-# AGENT_AUTO_APPROVED_SKILLS=create-pdf-file,create-word-file
\ No newline at end of file
+# AGENT_AUTO_APPROVED_SKILLS=create-pdf-file,create-word-file
diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
index 1d246bdc..17c63fbe 100644
--- a/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
+++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/SearchProviderOptions/index.jsx
@@ -248,6 +248,43 @@ export function BingSearchOptions({ settings }) {
);
}
+export function BaiduSearchOptions({ settings }) {
+ return (
+ <>
+
+ You can get an API key{" "}
+
+ from Baidu AI Cloud Qianfan.
+
+
+
+ >
+ );
+}
+
export function SerplySearchOptions({ settings }) {
return (
<>
diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/baidu.png b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/baidu.png
new file mode 100644
index 00000000..7d57538d
Binary files /dev/null and b/frontend/src/pages/Admin/Agents/WebSearchSelection/icons/baidu.png differ
diff --git a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
index 11a58edb..44803381 100644
--- a/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
+++ b/frontend/src/pages/Admin/Agents/WebSearchSelection/index.jsx
@@ -4,6 +4,7 @@ import SerpApiIcon from "./icons/serpapi.png";
import SearchApiIcon from "./icons/searchapi.png";
import SerperDotDevIcon from "./icons/serper.png";
import BingSearchIcon from "./icons/bing.png";
+import BaiduSearchIcon from "./icons/baidu.png";
import SerplySearchIcon from "./icons/serply.png";
import SearXNGSearchIcon from "./icons/searxng.png";
import TavilySearchIcon from "./icons/tavily.svg";
@@ -24,6 +25,7 @@ import {
SearchApiOptions,
SerperDotDevOptions,
BingSearchOptions,
+ BaiduSearchOptions,
SerplySearchOptions,
SearXNGOptions,
TavilySearchOptions,
@@ -71,6 +73,14 @@ const SEARCH_PROVIDERS = [
options: (settings) => ,
description: "Web search powered by the Bing Search API (paid service).",
},
+ {
+ name: "Baidu Search",
+ value: "baidu-search",
+ logo: BaiduSearchIcon,
+ options: (settings) => ,
+ description:
+ "Web search powered by Baidu Search for stronger zh-CN retrieval.",
+ },
{
name: "Serply.io",
value: "serply-engine",
diff --git a/server/.env.example b/server/.env.example
index bf5e519a..9c7878ad 100644
--- a/server/.env.example
+++ b/server/.env.example
@@ -382,6 +382,9 @@ TTS_PROVIDER="native"
#------ Bing Search ----------- https://portal.azure.com/
# AGENT_BING_SEARCH_API_KEY=
+#------ Baidu Search ----------- https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5
+# AGENT_BAIDU_SEARCH_API_KEY=
+
#------ Serply.io ----------- https://serply.io/
# AGENT_SERPLY_API_KEY=
diff --git a/server/models/systemSettings.js b/server/models/systemSettings.js
index c54a3213..4518740d 100644
--- a/server/models/systemSettings.js
+++ b/server/models/systemSettings.js
@@ -147,6 +147,7 @@ const SystemSettings = {
"searchapi",
"serper-dot-dev",
"bing-search",
+ "baidu-search",
"serply-engine",
"searxng-engine",
"tavily-search",
@@ -483,6 +484,7 @@ const SystemSettings = {
AgentSearchApiEngine: process.env.AGENT_SEARCHAPI_ENGINE || "google",
AgentSerperApiKey: !!process.env.AGENT_SERPER_DEV_KEY || null,
AgentBingSearchApiKey: !!process.env.AGENT_BING_SEARCH_API_KEY || null,
+ AgentBaiduSearchApiKey: !!process.env.AGENT_BAIDU_SEARCH_API_KEY || null,
AgentSerplyApiKey: !!process.env.AGENT_SERPLY_API_KEY || null,
AgentSearXNGApiUrl: process.env.AGENT_SEARXNG_API_URL || null,
AgentTavilyApiKey: !!process.env.AGENT_TAVILY_API_KEY || null,
diff --git a/server/utils/agents/aibitat/plugins/web-browsing.js b/server/utils/agents/aibitat/plugins/web-browsing.js
index 5f63fd21..23d5b38c 100644
--- a/server/utils/agents/aibitat/plugins/web-browsing.js
+++ b/server/utils/agents/aibitat/plugins/web-browsing.js
@@ -80,6 +80,9 @@ const webBrowsing = {
case "bing-search":
engine = "_bingWebSearch";
break;
+ case "baidu-search":
+ engine = "_baiduSearch";
+ break;
case "serply-engine":
engine = "_serplyEngine";
break;
@@ -604,6 +607,113 @@ const webBrowsing = {
);
return result;
},
+ _baiduSearch: async function (query) {
+ if (!process.env.AGENT_BAIDU_SEARCH_API_KEY) {
+ this.super.introspect(
+ `${this.caller}: I can't use Baidu Search because the user has not defined the required API key.\nVisit: https://cloud.baidu.com/doc/qianfan-api/s/Wmbq4z7e5 to create the API key.`
+ );
+ return `Search is disabled and no content was found. This functionality is disabled because the user has not set it up yet.`;
+ }
+
+ this.super.introspect(
+ `${this.caller}: Using Baidu Search to search for "${
+ query.length > 100 ? `${query.slice(0, 100)}...` : query
+ }"`
+ );
+
+ const { response, error } = await fetch(
+ "https://qianfan.baidubce.com/v2/ai_search/web_search",
+ {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${process.env.AGENT_BAIDU_SEARCH_API_KEY}`,
+ "X-Appbuilder-Authorization": `Bearer ${process.env.AGENT_BAIDU_SEARCH_API_KEY}`,
+ },
+ body: JSON.stringify({
+ messages: [{ role: "user", content: query }],
+ resource_type_filter: [{ type: "web", top_k: 10 }],
+ }),
+ }
+ )
+ .then(async (res) => {
+ if (res.ok) return res.json();
+
+ const body = await res.text().catch(() => "");
+ throw new Error(
+ `${res.status} - ${res.statusText}. params: ${JSON.stringify({
+ auth: this.middleTruncate(
+ process.env.AGENT_BAIDU_SEARCH_API_KEY,
+ 5
+ ),
+ q: query,
+ body: body.slice(0, 300),
+ })}`
+ );
+ })
+ .then((data) => {
+ return { response: data, error: null };
+ })
+ .catch((e) => {
+ this.super.handlerProps.log(`Baidu Search Error: ${e.message}`);
+ return { response: null, error: e.message };
+ });
+
+ if (error)
+ return `There was an error searching for content. ${error}`;
+
+ if (
+ (response?.code || response?.message) &&
+ !response?.references
+ ) {
+ return `There was an error searching for content. ${response?.message || response?.code}`;
+ }
+
+ /**
+ * Normalize Baidu Search References to the expected search results format
+ * @param {Array} references - The references to normalize
+ * @returns {Array} The normalized references
+ */
+ function normalizeBaiduSearchReferences(references = []) {
+ if (!Array.isArray(references)) return [];
+
+ const seenLinks = new Set();
+ return references
+ .filter((reference) => {
+ if (!reference) return false;
+ const referenceType = String(
+ reference.type || reference.resource_type || "web"
+ ).toLowerCase();
+ return referenceType === "web";
+ })
+ .map((reference) => {
+ const title = String(
+ reference.title || reference.web_anchor || ""
+ ).trim();
+ const link = String(reference.url || "").trim();
+ const snippet = String(
+ reference.snippet || reference.content || ""
+ ).trim();
+
+ if (!title || !link || seenLinks.has(link)) return null;
+ seenLinks.add(link);
+
+ return { title, link, snippet };
+ })
+ .filter(Boolean);
+ }
+
+ const data = normalizeBaiduSearchReferences(response?.references);
+ if (data.length === 0)
+ return `No information was found online for the search query.`;
+
+ this.reportSearchResultsCitations(data);
+ const result = JSON.stringify(data);
+ this.super.introspect(
+ `${this.caller}: I found ${data.length} results - reviewing the results now. (~${this.countTokens(result)} tokens)`
+ );
+ return result;
+ },
_serplyEngine: async function (
query,
language = "en",
diff --git a/server/utils/helpers/updateENV.js b/server/utils/helpers/updateENV.js
index ec171e05..de3630c1 100644
--- a/server/utils/helpers/updateENV.js
+++ b/server/utils/helpers/updateENV.js
@@ -586,6 +586,10 @@ const KEY_MAPPING = {
envKey: "AGENT_BING_SEARCH_API_KEY",
checks: [],
},
+ AgentBaiduSearchApiKey: {
+ envKey: "AGENT_BAIDU_SEARCH_API_KEY",
+ checks: [],
+ },
AgentSerplyApiKey: {
envKey: "AGENT_SERPLY_API_KEY",
checks: [],