From 5fa3b8d7a06a4718043c094e2521f5033d2a545a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Tue, 7 Apr 2026 23:02:10 +0100 Subject: [PATCH] refactor: dedupe provider trimmed readers --- extensions/chutes/models.ts | 9 ++++--- extensions/exa/src/exa-web-search-provider.ts | 18 +++++++------ .../google/src/gemini-web-search-provider.ts | 3 ++- extensions/kimi-coding/index.ts | 5 ++-- extensions/ollama/src/web-search-provider.ts | 9 ++++--- .../src/perplexity-web-search-provider.ts | 25 +++++++++++-------- extensions/tavily/src/config.ts | 3 ++- extensions/thread-ownership/index.ts | 9 +++---- extensions/zai/onboard.ts | 8 +++--- 9 files changed, 50 insertions(+), 39 deletions(-) diff --git a/extensions/chutes/models.ts b/extensions/chutes/models.ts index 0cda987724e..4b2718d05dd 100644 --- a/extensions/chutes/models.ts +++ b/extensions/chutes/models.ts @@ -1,6 +1,9 @@ import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared"; import { createSubsystemLogger } from "openclaw/plugin-sdk/runtime-env"; -import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "openclaw/plugin-sdk/text-runtime"; const log = createSubsystemLogger("chutes-models"); @@ -507,7 +510,7 @@ function cacheAndReturn( } export async function discoverChutesModels(accessToken?: string): Promise { - const trimmedKey = accessToken?.trim() ?? ""; + const trimmedKey = normalizeOptionalString(accessToken) ?? ""; const now = Date.now(); pruneExpiredCacheEntries(now); const cached = modelCache.get(trimmedKey); @@ -559,7 +562,7 @@ export async function discoverChutesModels(accessToken?: string): Promise (typeof entry === "string" ? entry.trim() : "")) - .filter(Boolean) + .map((entry) => normalizeOptionalString(entry)) + .filter((entry): entry is string => Boolean(entry)) .join("\n"); if (highlightText) { return highlightText; } } - if (typeof result.summary === "string" && result.summary.trim()) { - return result.summary.trim(); + const summary = normalizeOptionalString(result.summary); + if (summary) { + return summary; } - return typeof result.text === "string" ? result.text.trim() : ""; + return normalizeOptionalString(result.text) ?? ""; } function parsePositiveInteger(value: unknown): number | undefined { @@ -558,7 +562,7 @@ function createExaToolDefinition( const title = typeof entry.title === "string" ? entry.title : ""; const url = typeof entry.url === "string" ? entry.url : ""; const description = resolveExaDescription(entry); - const summary = typeof entry.summary === "string" ? entry.summary.trim() : ""; + const summary = normalizeOptionalString(entry.summary) ?? ""; const highlightScores = Array.isArray(entry.highlightScores) ? entry.highlightScores.filter( (score): score is number => typeof score === "number" && Number.isFinite(score), diff --git a/extensions/google/src/gemini-web-search-provider.ts b/extensions/google/src/gemini-web-search-provider.ts index 5c38fde1982..c10e6ba437a 100644 --- a/extensions/google/src/gemini-web-search-provider.ts +++ b/extensions/google/src/gemini-web-search-provider.ts @@ -25,6 +25,7 @@ import { wrapWebContent, writeCachedSearchPayload, } from "openclaw/plugin-sdk/provider-web-search"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { DEFAULT_GOOGLE_API_BASE_URL } from "../api.js"; const DEFAULT_GEMINI_MODEL = "gemini-2.5-flash"; @@ -73,7 +74,7 @@ function resolveGeminiApiKey(gemini?: GeminiConfig): string | undefined { } function resolveGeminiModel(gemini?: GeminiConfig): string { - const model = typeof gemini?.model === "string" ? gemini.model.trim() : ""; + const model = normalizeOptionalString(gemini?.model) ?? ""; return model || DEFAULT_GEMINI_MODEL; } diff --git a/extensions/kimi-coding/index.ts b/extensions/kimi-coding/index.ts index cf7461f922e..40e2f6a9512 100644 --- a/extensions/kimi-coding/index.ts +++ b/extensions/kimi-coding/index.ts @@ -2,7 +2,7 @@ import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry"; import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key"; import { normalizeProviderId } from "openclaw/plugin-sdk/provider-model-shared"; import type { SecretInput } from "openclaw/plugin-sdk/secret-input"; -import { isRecord } from "openclaw/plugin-sdk/text-runtime"; +import { isRecord, normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { applyKimiCodeConfig, KIMI_CODING_MODEL_REF } from "./onboard.js"; import { buildKimiCodingProvider } from "./provider-catalog.js"; import { KIMI_REPLAY_POLICY } from "./replay-policy.js"; @@ -80,8 +80,7 @@ export default definePluginEntry({ PROVIDER_ID, ); const builtInProvider = buildKimiCodingProvider(); - const explicitBaseUrl = - typeof explicitProvider?.baseUrl === "string" ? explicitProvider.baseUrl.trim() : ""; + const explicitBaseUrl = normalizeOptionalString(explicitProvider?.baseUrl) ?? ""; const explicitHeaders = isRecord(explicitProvider?.headers) ? (explicitProvider.headers as Record) : undefined; diff --git a/extensions/ollama/src/web-search-provider.ts b/extensions/ollama/src/web-search-provider.ts index 4ec1b04e122..3569a55a8cd 100644 --- a/extensions/ollama/src/web-search-provider.ts +++ b/extensions/ollama/src/web-search-provider.ts @@ -17,6 +17,7 @@ import { type WebSearchProviderPlugin, } from "openclaw/plugin-sdk/provider-web-search"; import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/ssrf-runtime"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { OLLAMA_DEFAULT_BASE_URL } from "./defaults.js"; import { buildOllamaBaseUrlSsrFPolicy, @@ -64,7 +65,7 @@ function resolveOllamaWebSearchApiKey(config?: OpenClawConfig): string | undefin function resolveOllamaWebSearchBaseUrl(config?: OpenClawConfig): string { const configuredBaseUrl = config?.models?.providers?.ollama?.baseUrl; - if (typeof configuredBaseUrl === "string" && configuredBaseUrl.trim()) { + if (normalizeOptionalString(configuredBaseUrl)) { return resolveOllamaApiBase(configuredBaseUrl); } return OLLAMA_DEFAULT_BASE_URL; @@ -73,14 +74,14 @@ function resolveOllamaWebSearchBaseUrl(config?: OpenClawConfig): string { function normalizeOllamaWebSearchResult( result: OllamaWebSearchResult, ): { title: string; url: string; content: string } | null { - const url = typeof result.url === "string" ? result.url.trim() : ""; + const url = normalizeOptionalString(result.url) ?? ""; if (!url) { return null; } return { - title: typeof result.title === "string" ? result.title.trim() : "", + title: normalizeOptionalString(result.title) ?? "", url, - content: typeof result.content === "string" ? result.content.trim() : "", + content: normalizeOptionalString(result.content) ?? "", }; } diff --git a/extensions/perplexity/src/perplexity-web-search-provider.ts b/extensions/perplexity/src/perplexity-web-search-provider.ts index 3af774afe08..ad9748ce5b2 100644 --- a/extensions/perplexity/src/perplexity-web-search-provider.ts +++ b/extensions/perplexity/src/perplexity-web-search-provider.ts @@ -32,7 +32,10 @@ import { wrapWebContent, writeCachedSearchPayload, } from "openclaw/plugin-sdk/provider-web-search"; -import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime"; +import { + normalizeLowercaseStringOrEmpty, + normalizeOptionalString, +} from "openclaw/plugin-sdk/text-runtime"; const DEFAULT_PERPLEXITY_BASE_URL = "https://openrouter.ai/api/v1"; const PERPLEXITY_DIRECT_BASE_URL = "https://api.perplexity.ai"; @@ -123,7 +126,7 @@ function resolvePerplexityBaseUrl( authSource: "config" | "perplexity_env" | "openrouter_env" | "none" = "none", configuredKey?: string, ): string { - const fromConfig = typeof perplexity?.baseUrl === "string" ? perplexity.baseUrl.trim() : ""; + const fromConfig = normalizeOptionalString(perplexity?.baseUrl) ?? ""; if (fromConfig) { return fromConfig; } @@ -142,7 +145,7 @@ function resolvePerplexityBaseUrl( } function resolvePerplexityModel(perplexity?: PerplexityConfig): string { - const model = typeof perplexity?.model === "string" ? perplexity.model.trim() : ""; + const model = normalizeOptionalString(perplexity?.model) ?? ""; return model || DEFAULT_PERPLEXITY_MODEL; } @@ -174,8 +177,7 @@ function resolvePerplexityTransport(perplexity?: PerplexityConfig): { const baseUrl = resolvePerplexityBaseUrl(perplexity, auth.source, auth.apiKey); const model = resolvePerplexityModel(perplexity); const hasLegacyOverride = Boolean( - (perplexity?.baseUrl && perplexity.baseUrl.trim()) || - (perplexity?.model && perplexity.model.trim()), + normalizeOptionalString(perplexity?.baseUrl) || normalizeOptionalString(perplexity?.model), ); return { ...auth, @@ -187,8 +189,8 @@ function resolvePerplexityTransport(perplexity?: PerplexityConfig): { } function extractPerplexityCitations(data: PerplexitySearchResponse): string[] { - const topLevel = (data.citations ?? []).filter( - (url): url is string => typeof url === "string" && Boolean(url.trim()), + const topLevel = (data.citations ?? []).filter((url): url is string => + Boolean(normalizeOptionalString(url)), ); if (topLevel.length > 0) { return [...new Set(topLevel)]; @@ -205,8 +207,9 @@ function extractPerplexityCitations(data: PerplexitySearchResponse): string[] { : typeof annotation.url === "string" ? annotation.url : undefined; - if (url?.trim()) { - citations.push(url.trim()); + const normalizedUrl = normalizeOptionalString(url); + if (normalizedUrl) { + citations.push(normalizedUrl); } } } @@ -344,8 +347,8 @@ function resolveRuntimeTransport(params: { perplexity && typeof perplexity === "object" && !Array.isArray(perplexity) ? (perplexity as { baseUrl?: string; model?: string }) : undefined; - const configuredBaseUrl = typeof scoped?.baseUrl === "string" ? scoped.baseUrl.trim() : ""; - const configuredModel = typeof scoped?.model === "string" ? scoped.model.trim() : ""; + const configuredBaseUrl = normalizeOptionalString(scoped?.baseUrl) ?? ""; + const configuredModel = normalizeOptionalString(scoped?.model) ?? ""; const baseUrl = (() => { if (configuredBaseUrl) { return configuredBaseUrl; diff --git a/extensions/tavily/src/config.ts b/extensions/tavily/src/config.ts index b2c1405a828..bcd5f7bdf71 100644 --- a/extensions/tavily/src/config.ts +++ b/extensions/tavily/src/config.ts @@ -3,6 +3,7 @@ import { normalizeResolvedSecretInputString, normalizeSecretInput, } from "openclaw/plugin-sdk/secret-input"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; export const DEFAULT_TAVILY_BASE_URL = "https://api.tavily.com"; export const DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS = 30; @@ -52,7 +53,7 @@ export function resolveTavilyApiKey(cfg?: OpenClawConfig): string | undefined { export function resolveTavilyBaseUrl(cfg?: OpenClawConfig): string { const search = resolveTavilySearchConfig(cfg); const configured = - (typeof search?.baseUrl === "string" ? search.baseUrl.trim() : "") || + (normalizeOptionalString(search?.baseUrl) ?? "") || normalizeSecretInput(process.env.TAVILY_BASE_URL) || ""; return configured || DEFAULT_TAVILY_BASE_URL; diff --git a/extensions/thread-ownership/index.ts b/extensions/thread-ownership/index.ts index c2bba7df07d..ebd8df2a20c 100644 --- a/extensions/thread-ownership/index.ts +++ b/extensions/thread-ownership/index.ts @@ -1,3 +1,4 @@ +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { definePluginEntry, fetchWithSsrFGuard, @@ -35,11 +36,9 @@ function resolveOwnershipAgent(config: OpenClawConfig): { id: string; name: stri : []; const selected = list.find((entry) => entry.default === true) ?? list[0]; - const id = - typeof selected?.id === "string" && selected.id.trim() ? selected.id.trim() : "unknown"; - const identityName = - typeof selected?.identity?.name === "string" ? selected.identity.name.trim() : ""; - const fallbackName = typeof selected?.name === "string" ? selected.name.trim() : ""; + const id = normalizeOptionalString(selected?.id) ?? "unknown"; + const identityName = normalizeOptionalString(selected?.identity?.name) ?? ""; + const fallbackName = normalizeOptionalString(selected?.name) ?? ""; const name = identityName || fallbackName; return { id, name }; diff --git a/extensions/zai/onboard.ts b/extensions/zai/onboard.ts index 39357ac342d..e5040bc1406 100644 --- a/extensions/zai/onboard.ts +++ b/extensions/zai/onboard.ts @@ -2,6 +2,7 @@ import { applyProviderConfigWithModelCatalogPreset, type OpenClawConfig, } from "openclaw/plugin-sdk/provider-onboard"; +import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime"; import { buildZaiModelDefinition, resolveZaiBaseUrl, @@ -28,8 +29,7 @@ const ZAI_DEFAULT_MODELS = [ function resolveZaiPresetBaseUrl(cfg: OpenClawConfig, endpoint?: string): string { const existingProvider = cfg.models?.providers?.zai; - const existingBaseUrl = - typeof existingProvider?.baseUrl === "string" ? existingProvider.baseUrl.trim() : ""; + const existingBaseUrl = normalizeOptionalString(existingProvider?.baseUrl) ?? ""; return endpoint ? resolveZaiBaseUrl(endpoint) : existingBaseUrl || resolveZaiBaseUrl(); } @@ -38,7 +38,7 @@ function applyZaiPreset( params?: { endpoint?: string; modelId?: string }, primaryModelRef?: string, ): OpenClawConfig { - const modelId = params?.modelId?.trim() || ZAI_DEFAULT_MODEL_ID; + const modelId = normalizeOptionalString(params?.modelId) ?? ZAI_DEFAULT_MODEL_ID; const modelRef = `zai/${modelId}`; return applyProviderConfigWithModelCatalogPreset(cfg, { providerId: "zai", @@ -61,7 +61,7 @@ export function applyZaiConfig( cfg: OpenClawConfig, params?: { endpoint?: string; modelId?: string }, ): OpenClawConfig { - const modelId = params?.modelId?.trim() || ZAI_DEFAULT_MODEL_ID; + const modelId = normalizeOptionalString(params?.modelId) ?? ZAI_DEFAULT_MODEL_ID; const modelRef = modelId === ZAI_DEFAULT_MODEL_ID ? ZAI_DEFAULT_MODEL_REF : `zai/${modelId}`; return applyZaiPreset(cfg, params, modelRef); }