diff --git a/extensions/firecrawl/src/config.ts b/extensions/firecrawl/src/config.ts index 3a149266c57c..bbeac568d2b2 100644 --- a/extensions/firecrawl/src/config.ts +++ b/extensions/firecrawl/src/config.ts @@ -1,5 +1,6 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; import { canResolveEnvSecretRefInReadOnlyPath } from "openclaw/plugin-sdk/extension-shared"; +import { resolvePositiveTimeoutSeconds } from "openclaw/plugin-sdk/provider-web-fetch"; import { resolveSecretInputString, normalizeSecretInput } from "openclaw/plugin-sdk/secret-input"; export const DEFAULT_FIRECRAWL_BASE_URL = "https://api.firecrawl.dev"; @@ -225,23 +226,13 @@ export function resolveFirecrawlScrapeTimeoutSeconds( cfg?: OpenClawConfig, override?: number, ): number { - if (typeof override === "number" && Number.isFinite(override) && override > 0) { - return Math.floor(override); - } const fetch = resolveFirecrawlFetchConfig(cfg); - if ( - typeof fetch?.timeoutSeconds === "number" && - Number.isFinite(fetch.timeoutSeconds) && - fetch.timeoutSeconds > 0 - ) { - return Math.floor(fetch.timeoutSeconds); - } - return DEFAULT_FIRECRAWL_SCRAPE_TIMEOUT_SECONDS; + return resolvePositiveTimeoutSeconds( + override, + resolvePositiveTimeoutSeconds(fetch?.timeoutSeconds, DEFAULT_FIRECRAWL_SCRAPE_TIMEOUT_SECONDS), + ); } export function resolveFirecrawlSearchTimeoutSeconds(override?: number): number { - if (typeof override === "number" && Number.isFinite(override) && override > 0) { - return Math.floor(override); - } - return DEFAULT_FIRECRAWL_SEARCH_TIMEOUT_SECONDS; + return resolvePositiveTimeoutSeconds(override, DEFAULT_FIRECRAWL_SEARCH_TIMEOUT_SECONDS); } diff --git a/extensions/firecrawl/src/firecrawl-tools.test.ts b/extensions/firecrawl/src/firecrawl-tools.test.ts index af4906158b61..2f3c4d0e43c5 100644 --- a/extensions/firecrawl/src/firecrawl-tools.test.ts +++ b/extensions/firecrawl/src/firecrawl-tools.test.ts @@ -936,6 +936,9 @@ describe("firecrawl tools", () => { expect(resolveFirecrawlScrapeTimeoutSeconds(cfg)).toBe(42); expect(resolveFirecrawlScrapeTimeoutSeconds(cfg, 19.8)).toBe(19); expect(resolveFirecrawlSearchTimeoutSeconds(9.7)).toBe(9); + expect(resolveFirecrawlScrapeTimeoutSeconds(cfg, 0.5)).toBe(1); + expect(resolveFirecrawlScrapeTimeoutSeconds(cfg, 0)).toBe(42); + expect(resolveFirecrawlSearchTimeoutSeconds(0.5)).toBe(1); }); it("normalizes mixed search payload shapes into search items", () => { diff --git a/extensions/tavily/src/config.ts b/extensions/tavily/src/config.ts index de7cf5807f44..be91c8197600 100644 --- a/extensions/tavily/src/config.ts +++ b/extensions/tavily/src/config.ts @@ -1,4 +1,5 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts"; +import { resolvePositiveTimeoutSeconds } from "openclaw/plugin-sdk/provider-web-search"; import { normalizeResolvedSecretInputString, normalizeSecretInput, @@ -60,15 +61,9 @@ export function resolveTavilyBaseUrl(cfg?: OpenClawConfig): string { } export function resolveTavilySearchTimeoutSeconds(override?: number): number { - if (typeof override === "number" && Number.isFinite(override) && override > 0) { - return Math.floor(override); - } - return DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS; + return resolvePositiveTimeoutSeconds(override, DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS); } export function resolveTavilyExtractTimeoutSeconds(override?: number): number { - if (typeof override === "number" && Number.isFinite(override) && override > 0) { - return Math.floor(override); - } - return DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS; + return resolvePositiveTimeoutSeconds(override, DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS); } diff --git a/extensions/tavily/src/tavily-tools.test.ts b/extensions/tavily/src/tavily-tools.test.ts index 17f544386c53..eb39cdd93f7f 100644 --- a/extensions/tavily/src/tavily-tools.test.ts +++ b/extensions/tavily/src/tavily-tools.test.ts @@ -400,6 +400,8 @@ describe("tavily tools", () => { it("accepts positive numeric timeout overrides and floors them", () => { expect(resolveTavilySearchTimeoutSeconds(19.9)).toBe(19); expect(resolveTavilyExtractTimeoutSeconds(42.7)).toBe(42); + expect(resolveTavilySearchTimeoutSeconds(0.5)).toBe(1); + expect(resolveTavilyExtractTimeoutSeconds(0.5)).toBe(1); expect(resolveTavilySearchTimeoutSeconds(0)).toBe(DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS); expect(resolveTavilyExtractTimeoutSeconds(Number.NaN)).toBe( DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS, diff --git a/src/agents/tools/web-shared.ts b/src/agents/tools/web-shared.ts index 11fe364ea991..471afa4f5803 100644 --- a/src/agents/tools/web-shared.ts +++ b/src/agents/tools/web-shared.ts @@ -15,6 +15,12 @@ export function resolveTimeoutSeconds(value: unknown, fallback: number): number return Math.max(1, Math.floor(parsed)); } +export function resolvePositiveTimeoutSeconds(value: unknown, fallback: number): number { + const parsed = + typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback; + return Math.max(1, Math.floor(parsed)); +} + export function resolveCacheTtlMs(value: unknown, fallbackMinutes: number): number { const minutes = typeof value === "number" && Number.isFinite(value) ? Math.max(0, value) : fallbackMinutes; diff --git a/src/plugin-sdk/provider-web-fetch.ts b/src/plugin-sdk/provider-web-fetch.ts index 70b8c77bfa9f..d48584d04b69 100644 --- a/src/plugin-sdk/provider-web-fetch.ts +++ b/src/plugin-sdk/provider-web-fetch.ts @@ -19,6 +19,7 @@ export { readCache, readResponseText, resolveCacheTtlMs, + resolvePositiveTimeoutSeconds, resolveTimeoutSeconds, writeCache, } from "../agents/tools/web-shared.js"; diff --git a/src/plugin-sdk/provider-web-search.ts b/src/plugin-sdk/provider-web-search.ts index 12dceaa00d8d..202c965d9f2f 100644 --- a/src/plugin-sdk/provider-web-search.ts +++ b/src/plugin-sdk/provider-web-search.ts @@ -62,6 +62,7 @@ export { readCache, readResponseText, resolveCacheTtlMs, + resolvePositiveTimeoutSeconds, resolveTimeoutSeconds, writeCache, } from "../agents/tools/web-shared.js";