refactor: dedupe provider trimmed readers

This commit is contained in:
Peter Steinberger
2026-04-07 23:02:10 +01:00
parent 8fae182531
commit 5fa3b8d7a0
9 changed files with 50 additions and 39 deletions

View File

@@ -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<ModelDefinitionConfig[]> {
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<ModelD
const models: ModelDefinitionConfig[] = [];
for (const entry of data) {
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
const id = normalizeOptionalString(entry?.id) ?? "";
if (!id || seen.has(id)) {
continue;
}

View File

@@ -24,7 +24,10 @@ import {
wrapWebContent,
writeCachedSearchPayload,
} from "openclaw/plugin-sdk/provider-web-search";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "openclaw/plugin-sdk/text-runtime";
const EXA_SEARCH_ENDPOINT = "https://api.exa.ai/search";
const EXA_SEARCH_TYPES = ["auto", "neural", "fast", "deep", "deep-reasoning", "instant"] as const;
@@ -105,17 +108,18 @@ function resolveExaDescription(result: ExaSearchResult): string {
const highlights = result.highlights;
if (Array.isArray(highlights)) {
const highlightText = highlights
.map((entry) => (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),

View File

@@ -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;
}

View File

@@ -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<string, SecretInput>)
: undefined;

View File

@@ -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) ?? "",
};
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 };

View File

@@ -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);
}