refactor: dedupe core lowercase helpers

This commit is contained in:
Peter Steinberger
2026-04-07 14:26:15 +01:00
parent ad605052bf
commit a903936750
10 changed files with 27 additions and 18 deletions

View File

@@ -2,6 +2,7 @@ import type { ConversationRef } from "../infra/outbound/session-binding-service.
import { normalizeAccountId } from "../routing/session-key.js";
import { defaultRuntime } from "../runtime.js";
import { isCronSessionKey } from "../sessions/session-key-utils.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import {
mergeDeliveryContext,
normalizeDeliveryContext,
@@ -189,7 +190,7 @@ export async function resolveSubagentCompletionOrigin(params: {
expectsCompletionMessage: boolean;
}): Promise<DeliveryContext | undefined> {
const requesterOrigin = normalizeDeliveryContext(params.requesterOrigin);
const channel = requesterOrigin?.channel?.trim().toLowerCase();
const channel = normalizeOptionalLowercaseString(requesterOrigin?.channel);
const to = requesterOrigin?.to?.trim();
const accountId = normalizeAccountId(requesterOrigin?.accountId);
const threadId =

View File

@@ -12,7 +12,11 @@ import { formatErrorMessage } from "../../infra/errors.js";
import type { OutboundDeliveryResult } from "../../infra/outbound/deliver.js";
import { normalizeTargetForProvider } from "../../infra/outbound/target-normalization.js";
import { logWarn, logError } from "../../logger.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "../../shared/string-coerce.js";
import type { CronJob, CronRunTelemetry } from "../types.js";
import type { DeliveryTargetResolution } from "./delivery-target.js";
import { pickSummaryFromOutput } from "./helpers.js";
@@ -31,8 +35,8 @@ export function matchesMessagingToolDeliveryTarget(
if (!delivery.channel || !delivery.to || !target.to) {
return false;
}
const channel = delivery.channel.trim().toLowerCase();
const provider = target.provider?.trim().toLowerCase();
const channel = normalizeLowercaseStringOrEmpty(delivery.channel);
const provider = normalizeOptionalLowercaseString(target.provider);
if (provider && provider !== "message" && provider !== channel) {
return false;
}

View File

@@ -1,3 +1,5 @@
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
const SUBAGENT_FOLLOWUP_HINTS = [
"subagent spawned",
"spawned a subagent",
@@ -25,7 +27,7 @@ const INTERIM_CRON_HINTS = [
] as const;
function normalizeHintText(value: string): string {
return value.trim().toLowerCase().replace(/\s+/g, " ");
return normalizeLowercaseStringOrEmpty(value).replace(/\s+/g, " ");
}
export function isLikelyInterimCronMessage(value: string): boolean {

View File

@@ -3,6 +3,7 @@ import { estimateBase64DecodedBytes } from "../media/base64.js";
import type { PromptImageOrderEntry } from "../media/prompt-image-order.js";
import { sniffMimeFromBase64 } from "../media/sniff-mime-from-base64.js";
import { deleteMediaBuffer, saveMediaBuffer } from "../media/store.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
export type ChatAttachment = {
type?: string;
@@ -129,7 +130,7 @@ function normalizeMime(mime?: string): string | undefined {
if (!mime) {
return undefined;
}
const cleaned = mime.split(";")[0]?.trim().toLowerCase();
const cleaned = normalizeOptionalLowercaseString(mime.split(";")[0]);
return cleaned || undefined;
}

View File

@@ -16,7 +16,7 @@ import { buildOutboundSessionContext } from "../../infra/outbound/session-contex
import { maybeResolveIdLikeTarget } from "../../infra/outbound/target-resolver.js";
import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
import { normalizePollInput } from "../../polls.js";
import { readStringValue } from "../../shared/string-coerce.js";
import { normalizeOptionalLowercaseString, readStringValue } from "../../shared/string-coerce.js";
import {
ErrorCodes,
errorShape,
@@ -64,7 +64,7 @@ async function resolveRequestedChannel(params: {
const channelInput = readStringValue(params.requestChannel);
const normalizedChannel = channelInput ? normalizeChannelId(channelInput) : null;
if (channelInput && !normalizedChannel) {
const normalizedInput = channelInput.trim().toLowerCase();
const normalizedInput = normalizeOptionalLowercaseString(channelInput) ?? "";
if (params.rejectWebchatAsInternalOnly && normalizedInput === "webchat") {
return {
error: errorShape(
@@ -301,7 +301,7 @@ export const sendHandlers: GatewayRequestHandlers = {
);
const providedSessionKey =
typeof request.sessionKey === "string" && request.sessionKey.trim()
? request.sessionKey.trim().toLowerCase()
? (normalizeOptionalLowercaseString(request.sessionKey) ?? undefined)
: undefined;
const explicitAgentId =
typeof request.agentId === "string" && request.agentId.trim()

View File

@@ -203,8 +203,8 @@ function inferMimeType(
outputFormat: string | undefined,
fileExtension: string | undefined,
): string | undefined {
const normalizedOutput = outputFormat?.trim().toLowerCase();
const normalizedExtension = fileExtension?.trim().toLowerCase();
const normalizedOutput = normalizeOptionalString(outputFormat)?.toLowerCase();
const normalizedExtension = normalizeOptionalString(fileExtension)?.toLowerCase();
if (
normalizedOutput === "mp3" ||
normalizedOutput?.startsWith("mp3_") ||

View File

@@ -10,6 +10,7 @@ import {
resolveInputFileLimits,
} from "../media/input-files.js";
import { wrapExternalContent } from "../security/external-content.js";
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
import { resolveAttachmentKind } from "./attachments.js";
import { runWithConcurrency } from "./concurrency.js";
import { DEFAULT_ECHO_TRANSCRIPT_FORMAT, sendTranscriptEcho } from "./echo-transcript.js";
@@ -71,10 +72,7 @@ const TEXT_EXT_MIME = new Map<string, string>([
]);
function sanitizeMimeType(value?: string): string | undefined {
if (!value) {
return undefined;
}
const trimmed = value.trim().toLowerCase();
const trimmed = normalizeOptionalLowercaseString(value);
if (!trimmed) {
return undefined;
}

View File

@@ -1,6 +1,7 @@
import type { MsgContext } from "../auto-reply/templating.js";
import type { OpenClawConfig } from "../config/config.js";
import { logVerbose, shouldLogVerbose } from "../globals.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { isDeliverableMessageChannel } from "../utils/message-channel.js";
let deliverRuntimePromise: Promise<typeof import("../infra/outbound/deliver-runtime.js")> | null =
@@ -38,7 +39,7 @@ export async function sendTranscriptEcho(params: {
return;
}
const normalizedChannel = channel.trim().toLowerCase();
const normalizedChannel = normalizeLowercaseStringOrEmpty(channel);
if (!isDeliverableMessageChannel(normalizedChannel)) {
if (shouldLogVerbose()) {
logVerbose(

View File

@@ -24,6 +24,7 @@ import { resolveChannelInboundAttachmentRoots } from "../media/channel-inbound-r
import { mergeInboundPathRoots } from "../media/inbound-path-policy.js";
import { getDefaultMediaLocalRoots } from "../media/local-roots.js";
import { runExec } from "../process/exec.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { normalizeOptionalString } from "../shared/string-coerce.js";
import { MediaAttachmentCache, selectAttachments } from "./attachments.js";
import { resolveAutoMediaKeyProviders, resolveDefaultMediaModel } from "./defaults.js";
@@ -131,7 +132,7 @@ function resolveCatalogImageModelId(params: {
if (matches.length === 0) {
return undefined;
}
const autoEntry = matches.find((entry) => entry.id.trim().toLowerCase() === "auto");
const autoEntry = matches.find((entry) => normalizeLowercaseStringOrEmpty(entry.id) === "auto");
return normalizeOptionalString((autoEntry ?? matches[0])?.id);
}

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../config/config.js";
import type { SpeechProviderPlugin } from "../plugins/types.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { listSpeechProviders } from "./provider-registry.js";
import type {
SpeechModelOverridePolicy,
@@ -78,7 +79,7 @@ export function parseTtsDirectives(
const key = rawKey.toLowerCase();
if (key === "provider") {
if (policy.allowProvider) {
const providerId = rawValue.trim().toLowerCase();
const providerId = normalizeLowercaseStringOrEmpty(rawValue);
if (providerId) {
overrides.provider = providerId;
} else {