refactor: dedupe normalization lowercase helpers

This commit is contained in:
Peter Steinberger
2026-04-07 22:28:58 +01:00
parent fa82193c72
commit e0ad3e79e6
11 changed files with 29 additions and 19 deletions

View File

@@ -226,7 +226,7 @@ function resolveComfyNetworkPolicy(params: {
return {};
}
const hostname = normalizeOptionalString(parsed.hostname)?.toLowerCase() ?? "";
const hostname = normalizeOptionalLowercaseString(parsed.hostname) ?? "";
if (!hostname || !params.allowPrivateNetwork || !isPrivateOrLoopbackHost(hostname)) {
return {};
}

View File

@@ -7,6 +7,7 @@ import {
type ChannelMatchSource,
} from "openclaw/plugin-sdk/channel-targets";
import { evaluateGroupRouteAccessForPolicy } from "openclaw/plugin-sdk/group-access";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { formatDiscordUserTag } from "./format.js";
export type DiscordAllowList = {
@@ -84,9 +85,7 @@ export function normalizeDiscordAllowList(raw: string[] | undefined, prefixes: s
}
export function normalizeDiscordSlug(value: string) {
return value
.trim()
.toLowerCase()
return normalizeLowercaseStringOrEmpty(value)
.replace(/^#/, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");

View File

@@ -7,6 +7,7 @@ import type {
RESTPostAPIGuildScheduledEventJSONBody,
} from "discord-api-types/v10";
import { Routes } from "discord-api-types/v10";
import { normalizeOptionalLowercaseString } from "openclaw/plugin-sdk/text-runtime";
import { loadWebMediaRaw } from "openclaw/plugin-sdk/web-media";
import { resolveDiscordRest } from "./send.shared.js";
import type {
@@ -89,7 +90,7 @@ export async function resolveEventCoverImage(
const media = await loadWebMediaRaw(imageUrl, DISCORD_MAX_EVENT_COVER_BYTES, {
localRoots: opts?.localRoots,
});
const contentType = media.contentType?.toLowerCase();
const contentType = normalizeOptionalLowercaseString(media.contentType);
if (!contentType || !ALLOWED_EVENT_COVER_TYPES.has(contentType)) {
throw new Error(
`Discord event cover images must be PNG, JPG, or GIF (got ${contentType ?? "unknown"})`,

View File

@@ -11,6 +11,7 @@ import type * as LanceDB from "@lancedb/lancedb";
import { Type } from "@sinclair/typebox";
import OpenAI from "openai";
import { ensureGlobalUndiciEnvProxyDispatcher } from "openclaw/plugin-sdk/runtime-env";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import { definePluginEntry, type OpenClawPluginApi } from "./api.js";
import {
DEFAULT_CAPTURE_MAX_CHARS,
@@ -259,7 +260,7 @@ export function shouldCapture(text: string, options?: { maxChars?: number }): bo
}
export function detectCategory(text: string): MemoryCategory {
const lower = text.toLowerCase();
const lower = normalizeLowercaseStringOrEmpty(text);
if (/prefer|radši|like|love|hate|want/i.test(lower)) {
return "preference";
}

View File

@@ -4,6 +4,7 @@ import {
listDirectoryEntriesFromSources,
} from "openclaw/plugin-sdk/directory-runtime";
import { createLazyRuntimeNamedExport } from "openclaw/plugin-sdk/lazy-runtime";
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
import type { ChannelPlugin } from "./channel-api.js";
import { normalizeMSTeamsMessagingTarget } from "./resolve-allowlist.js";
import { resolveMSTeamsCredentials } from "./token.js";
@@ -33,7 +34,7 @@ export const msteamsDirectoryAdapter: NonNullable<ChannelPlugin["directory"]> =
limit,
normalizeId: (raw) => {
const normalized = normalizeMSTeamsMessagingTarget(raw) ?? raw;
const lowered = normalized.toLowerCase();
const lowered = normalizeLowercaseStringOrEmpty(normalized);
if (lowered.startsWith("user:") || lowered.startsWith("conversation:")) {
return normalized;
}

View File

@@ -1,3 +1,5 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
const REQUIRED_DISCOVERY_REFS = [
"repo/qa/seed-scenarios.json",
"repo/qa/QA_KICKOFF_TASK.md",
@@ -5,6 +7,8 @@ const REQUIRED_DISCOVERY_REFS = [
"repo/docs/help/testing.md",
] as const;
const REQUIRED_DISCOVERY_REFS_LOWER = REQUIRED_DISCOVERY_REFS.map(normalizeLowercaseStringOrEmpty);
const DISCOVERY_SCOPE_LEAK_PHRASES = [
"all mandatory scenarios",
"final qa tally",
@@ -15,8 +19,8 @@ const DISCOVERY_SCOPE_LEAK_PHRASES = [
] as const;
function confirmsDiscoveryFileRead(text: string) {
const lower = text.toLowerCase();
const mentionsAllRefs = REQUIRED_DISCOVERY_REFS.every((ref) => lower.includes(ref.toLowerCase()));
const lower = normalizeLowercaseStringOrEmpty(text);
const mentionsAllRefs = REQUIRED_DISCOVERY_REFS_LOWER.every((ref) => lower.includes(ref));
const confirmsRead =
/(?:read|retrieved|inspected|loaded|accessed|digested)\s+all\s+(?:four|4)\s+(?:(?:requested|required|mandated|seeded)\s+)?files/.test(
lower,
@@ -29,7 +33,7 @@ function confirmsDiscoveryFileRead(text: string) {
}
export function hasDiscoveryLabels(text: string) {
const lower = text.toLowerCase();
const lower = normalizeLowercaseStringOrEmpty(text);
return (
lower.includes("worked") &&
lower.includes("failed") &&
@@ -39,7 +43,7 @@ export function hasDiscoveryLabels(text: string) {
}
export function reportsMissingDiscoveryFiles(text: string) {
const lower = text.toLowerCase();
const lower = normalizeLowercaseStringOrEmpty(text);
if (confirmsDiscoveryFileRead(text)) {
return false;
}
@@ -52,6 +56,6 @@ export function reportsMissingDiscoveryFiles(text: string) {
}
export function reportsDiscoveryScopeLeak(text: string) {
const lower = text.toLowerCase();
const lower = normalizeLowercaseStringOrEmpty(text);
return DISCOVERY_SCOPE_LEAK_PHRASES.some((phrase) => lower.includes(phrase));
}

View File

@@ -1,5 +1,7 @@
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
export function hasModelSwitchContinuityEvidence(text: string) {
const lower = text.toLowerCase();
const lower = normalizeLowercaseStringOrEmpty(text);
const mentionsHandoff =
lower.includes("handoff") || lower.includes("model switch") || lower.includes("switched");
const mentionsKickoffTask =

View File

@@ -2372,7 +2372,7 @@ When the user asks for the hot install marker exactly, reply with exactly: HOT-I
(candidate) =>
candidate.direction === "outbound" &&
candidate.conversation.id === "qa-operator" &&
candidate.text.toLowerCase().includes("lighthouse"),
normalizeLowercaseStringOrEmpty(candidate.text).includes("lighthouse"),
)
.at(-1),
liveTurnTimeoutMs(env, 45_000),

View File

@@ -236,7 +236,9 @@ export async function listPage(state: CronServiceState, opts?: CronListPageOptio
if (!query) {
return true;
}
const haystack = [job.name, job.description ?? "", job.agentId ?? ""].join(" ").toLowerCase();
const haystack = normalizeLowercaseStringOrEmpty(
[job.name, job.description ?? "", job.agentId ?? ""].join(" "),
);
return haystack.includes(query);
});
const sorted = sortJobs(filtered, sortBy, sortDir);

View File

@@ -1,3 +1,4 @@
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import {
installLaunchAgent,
isLaunchAgentLoaded,
@@ -154,7 +155,7 @@ export function describeGatewayServiceRestart(
return {
scheduled: true,
daemonActionResult: "scheduled",
message: `restart scheduled, ${serviceNoun.toLowerCase()} will restart momentarily`,
message: `restart scheduled, ${normalizeLowercaseStringOrEmpty(serviceNoun)} will restart momentarily`,
progressMessage: `${serviceNoun} service restart scheduled.`,
};
}

View File

@@ -16,6 +16,7 @@ import { parseModelRef } from "../agents/model-selection.js";
import { runEmbeddedPiAgent } from "../agents/pi-embedded.js";
import type { OpenClawConfig } from "../config/config.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
const log = createSubsystemLogger("llm-slug-generator");
@@ -70,9 +71,7 @@ Reply with ONLY the slug, nothing else. Examples: "vendor-pitch", "api-design",
const text = result.payloads[0]?.text;
if (text) {
// Clean up the response - extract just the slug
const slug = text
.trim()
.toLowerCase()
const slug = normalizeLowercaseStringOrEmpty(text)
.replace(/[^a-z0-9-]/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "")