feat: add proxy support for web_search tool

Add HTTP/HTTPS proxy support for web_search tool using undici's ProxyAgent.
This enables web_search to work correctly when running behind corporate
proxies or VPNs.

Changes:
- Import ProxyAgent from undici
- Add getProxyDispatcher() helper function to read proxy from environment
- Apply proxy dispatcher to both Brave Search and Perplexity API calls
- Respects HTTP_PROXY and HTTPS_PROXY environment variables

Fixes issues for users running Moltbot behind proxies or VPNs who were
experiencing "fetch failed" errors when using web_search.
This commit is contained in:
zxz0622 2026-01-29 00:08:31 +08:00
parent 01e0d3a320
commit ec8869d87f

View File

@ -1,4 +1,5 @@
import { Type } from "@sinclair/typebox"; import { Type } from "@sinclair/typebox";
import { ProxyAgent } from "undici";
import type { MoltbotConfig } from "../../config/config.js"; import type { MoltbotConfig } from "../../config/config.js";
import { formatCliCommand } from "../../cli/command-format.js"; import { formatCliCommand } from "../../cli/command-format.js";
@ -32,6 +33,18 @@ const SEARCH_CACHE = new Map<string, CacheEntry<Record<string, unknown>>>();
const BRAVE_FRESHNESS_SHORTCUTS = new Set(["pd", "pw", "pm", "py"]); const BRAVE_FRESHNESS_SHORTCUTS = new Set(["pd", "pw", "pm", "py"]);
const BRAVE_FRESHNESS_RANGE = /^(\d{4}-\d{2}-\d{2})to(\d{4}-\d{2}-\d{2})$/; const BRAVE_FRESHNESS_RANGE = /^(\d{4}-\d{2}-\d{2})to(\d{4}-\d{2}-\d{2})$/;
/**
* Get proxy dispatcher from environment variables for HTTP requests.
* Respects HTTP_PROXY, HTTPS_PROXY environment variables.
*/
function getProxyDispatcher(): ProxyAgent | undefined {
const proxyUrl = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
if (proxyUrl) {
return new ProxyAgent(proxyUrl);
}
return undefined;
}
const WebSearchSchema = Type.Object({ const WebSearchSchema = Type.Object({
query: Type.String({ description: "Search query string." }), query: Type.String({ description: "Search query string." }),
count: Type.Optional( count: Type.Optional(
@ -273,6 +286,7 @@ async function runPerplexitySearch(params: {
timeoutSeconds: number; timeoutSeconds: number;
}): Promise<{ content: string; citations: string[] }> { }): Promise<{ content: string; citations: string[] }> {
const endpoint = `${params.baseUrl.replace(/\/$/, "")}/chat/completions`; const endpoint = `${params.baseUrl.replace(/\/$/, "")}/chat/completions`;
const proxyDispatcher = getProxyDispatcher();
const res = await fetch(endpoint, { const res = await fetch(endpoint, {
method: "POST", method: "POST",
@ -292,6 +306,7 @@ async function runPerplexitySearch(params: {
], ],
}), }),
signal: withTimeout(undefined, params.timeoutSeconds * 1000), signal: withTimeout(undefined, params.timeoutSeconds * 1000),
...(proxyDispatcher && { dispatcher: proxyDispatcher }),
}); });
if (!res.ok) { if (!res.ok) {
@ -371,6 +386,7 @@ async function runWebSearch(params: {
url.searchParams.set("freshness", params.freshness); url.searchParams.set("freshness", params.freshness);
} }
const proxyDispatcher = getProxyDispatcher();
const res = await fetch(url.toString(), { const res = await fetch(url.toString(), {
method: "GET", method: "GET",
headers: { headers: {
@ -378,6 +394,7 @@ async function runWebSearch(params: {
"X-Subscription-Token": params.apiKey, "X-Subscription-Token": params.apiKey,
}, },
signal: withTimeout(undefined, params.timeoutSeconds * 1000), signal: withTimeout(undefined, params.timeoutSeconds * 1000),
...(proxyDispatcher && { dispatcher: proxyDispatcher }),
}); });
if (!res.ok) { if (!res.ok) {