mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-14 04:18:35 +00:00
perf(runtime): trim hot-path allocations and cache channel plugin lookups
This commit is contained in:
@@ -469,33 +469,31 @@ async function deliverOutboundPayloadsCore(
|
||||
text: normalizedText,
|
||||
};
|
||||
};
|
||||
const normalizedPayloads = normalizeReplyPayloadsForDelivery(payloads)
|
||||
.map((payload) => {
|
||||
// Strip HTML tags for plain-text surfaces (WhatsApp, Signal, etc.)
|
||||
// Models occasionally produce <br>, <b>, etc. that render as literal text.
|
||||
// See https://github.com/openclaw/openclaw/issues/31884
|
||||
if (!isPlainTextSurface(channel) || !payload.text) {
|
||||
return payload;
|
||||
}
|
||||
const normalizedPayloads: ReplyPayload[] = [];
|
||||
for (const payload of normalizeReplyPayloadsForDelivery(payloads)) {
|
||||
let sanitizedPayload = payload;
|
||||
// Strip HTML tags for plain-text surfaces (WhatsApp, Signal, etc.)
|
||||
// Models occasionally produce <br>, <b>, etc. that render as literal text.
|
||||
// See https://github.com/openclaw/openclaw/issues/31884
|
||||
if (isPlainTextSurface(channel) && payload.text) {
|
||||
// Telegram sendPayload uses textMode:"html". Preserve raw HTML in this path.
|
||||
if (channel === "telegram" && payload.channelData) {
|
||||
return payload;
|
||||
if (!(channel === "telegram" && payload.channelData)) {
|
||||
sanitizedPayload = { ...payload, text: sanitizeForPlainText(payload.text) };
|
||||
}
|
||||
return { ...payload, text: sanitizeForPlainText(payload.text) };
|
||||
})
|
||||
.flatMap((payload) => {
|
||||
const normalized = normalizePayloadForChannelDelivery(payload, channel);
|
||||
return normalized ? [normalized] : [];
|
||||
});
|
||||
}
|
||||
const normalized = normalizePayloadForChannelDelivery(sanitizedPayload, channel);
|
||||
if (normalized) {
|
||||
normalizedPayloads.push(normalized);
|
||||
}
|
||||
}
|
||||
const hookRunner = getGlobalHookRunner();
|
||||
const sessionKeyForInternalHooks = params.mirror?.sessionKey ?? params.session?.key;
|
||||
const mirrorIsGroup = params.mirror?.isGroup;
|
||||
const mirrorGroupId = params.mirror?.groupId;
|
||||
if (
|
||||
hookRunner?.hasHooks("message_sent") &&
|
||||
params.session?.agentId &&
|
||||
!sessionKeyForInternalHooks
|
||||
) {
|
||||
const hasMessageSentHooks = hookRunner?.hasHooks("message_sent") ?? false;
|
||||
const hasMessageSendingHooks = hookRunner?.hasHooks("message_sending") ?? false;
|
||||
const canEmitInternalHook = Boolean(sessionKeyForInternalHooks);
|
||||
if (hasMessageSentHooks && params.session?.agentId && !sessionKeyForInternalHooks) {
|
||||
log.warn(
|
||||
"deliverOutboundPayloads: session.agentId present without session key; internal message:sent hook will be skipped",
|
||||
{
|
||||
@@ -517,6 +515,9 @@ async function deliverOutboundPayloadsCore(
|
||||
error?: string;
|
||||
messageId?: string;
|
||||
}) => {
|
||||
if (!hasMessageSentHooks && !canEmitInternalHook) {
|
||||
return;
|
||||
}
|
||||
const canonical = buildCanonicalSentMessageHookContext({
|
||||
to,
|
||||
content: params.content,
|
||||
@@ -529,9 +530,9 @@ async function deliverOutboundPayloadsCore(
|
||||
isGroup: mirrorIsGroup,
|
||||
groupId: mirrorGroupId,
|
||||
});
|
||||
if (hookRunner?.hasHooks("message_sent")) {
|
||||
if (hasMessageSentHooks) {
|
||||
fireAndForgetHook(
|
||||
hookRunner.runMessageSent(
|
||||
hookRunner!.runMessageSent(
|
||||
toPluginMessageSentEvent(canonical),
|
||||
toPluginMessageContext(canonical),
|
||||
),
|
||||
@@ -541,7 +542,7 @@ async function deliverOutboundPayloadsCore(
|
||||
},
|
||||
);
|
||||
}
|
||||
if (!sessionKeyForInternalHooks) {
|
||||
if (!canEmitInternalHook) {
|
||||
return;
|
||||
}
|
||||
fireAndForgetHook(
|
||||
@@ -549,7 +550,7 @@ async function deliverOutboundPayloadsCore(
|
||||
createInternalHookEvent(
|
||||
"message",
|
||||
"sent",
|
||||
sessionKeyForInternalHooks,
|
||||
sessionKeyForInternalHooks!,
|
||||
toInternalMessageSentContext(canonical),
|
||||
),
|
||||
),
|
||||
@@ -564,9 +565,9 @@ async function deliverOutboundPayloadsCore(
|
||||
|
||||
// Run message_sending plugin hook (may modify content or cancel)
|
||||
let effectivePayload = payload;
|
||||
if (hookRunner?.hasHooks("message_sending")) {
|
||||
if (hasMessageSendingHooks) {
|
||||
try {
|
||||
const sendingResult = await hookRunner.runMessageSending(
|
||||
const sendingResult = await hookRunner!.runMessageSending(
|
||||
{
|
||||
to,
|
||||
content: payloadSummary.text,
|
||||
|
||||
@@ -43,9 +43,10 @@ function mergeMediaUrls(...lists: Array<ReadonlyArray<string | undefined> | unde
|
||||
export function normalizeReplyPayloadsForDelivery(
|
||||
payloads: readonly ReplyPayload[],
|
||||
): ReplyPayload[] {
|
||||
return payloads.flatMap((payload) => {
|
||||
const normalized: ReplyPayload[] = [];
|
||||
for (const payload of payloads) {
|
||||
if (shouldSuppressReasoningPayload(payload)) {
|
||||
return [];
|
||||
continue;
|
||||
}
|
||||
const parsed = parseReplyDirectives(payload.text ?? "");
|
||||
const explicitMediaUrls = payload.mediaUrls ?? parsed.mediaUrls;
|
||||
@@ -67,47 +68,50 @@ export function normalizeReplyPayloadsForDelivery(
|
||||
audioAsVoice: Boolean(payload.audioAsVoice || parsed.audioAsVoice),
|
||||
};
|
||||
if (parsed.isSilent && mergedMedia.length === 0) {
|
||||
return [];
|
||||
continue;
|
||||
}
|
||||
if (!isRenderablePayload(next)) {
|
||||
return [];
|
||||
continue;
|
||||
}
|
||||
return [next];
|
||||
});
|
||||
normalized.push(next);
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function normalizeOutboundPayloads(
|
||||
payloads: readonly ReplyPayload[],
|
||||
): NormalizedOutboundPayload[] {
|
||||
return normalizeReplyPayloadsForDelivery(payloads)
|
||||
.map((payload) => {
|
||||
const channelData = payload.channelData;
|
||||
const normalized: NormalizedOutboundPayload = {
|
||||
text: payload.text ?? "",
|
||||
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []),
|
||||
};
|
||||
if (channelData && Object.keys(channelData).length > 0) {
|
||||
normalized.channelData = channelData;
|
||||
}
|
||||
return normalized;
|
||||
})
|
||||
.filter(
|
||||
(payload) =>
|
||||
payload.text ||
|
||||
payload.mediaUrls.length > 0 ||
|
||||
Boolean(payload.channelData && Object.keys(payload.channelData).length > 0),
|
||||
);
|
||||
const normalizedPayloads: NormalizedOutboundPayload[] = [];
|
||||
for (const payload of normalizeReplyPayloadsForDelivery(payloads)) {
|
||||
const mediaUrls = payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
||||
const channelData = payload.channelData;
|
||||
const hasChannelData = Boolean(channelData && Object.keys(channelData).length > 0);
|
||||
const text = payload.text ?? "";
|
||||
if (!text && mediaUrls.length === 0 && !hasChannelData) {
|
||||
continue;
|
||||
}
|
||||
normalizedPayloads.push({
|
||||
text,
|
||||
mediaUrls,
|
||||
...(hasChannelData ? { channelData } : {}),
|
||||
});
|
||||
}
|
||||
return normalizedPayloads;
|
||||
}
|
||||
|
||||
export function normalizeOutboundPayloadsForJson(
|
||||
payloads: readonly ReplyPayload[],
|
||||
): OutboundPayloadJson[] {
|
||||
return normalizeReplyPayloadsForDelivery(payloads).map((payload) => ({
|
||||
text: payload.text ?? "",
|
||||
mediaUrl: payload.mediaUrl ?? null,
|
||||
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : undefined),
|
||||
channelData: payload.channelData,
|
||||
}));
|
||||
const normalized: OutboundPayloadJson[] = [];
|
||||
for (const payload of normalizeReplyPayloadsForDelivery(payloads)) {
|
||||
normalized.push({
|
||||
text: payload.text ?? "",
|
||||
mediaUrl: payload.mediaUrl ?? null,
|
||||
mediaUrls: payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : undefined),
|
||||
channelData: payload.channelData,
|
||||
});
|
||||
}
|
||||
return normalized;
|
||||
}
|
||||
|
||||
export function formatOutboundPayloadLog(
|
||||
|
||||
Reference in New Issue
Block a user