UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

116 lines (115 loc) • 5.94 kB
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-1VuiLaaR.mjs"; import { a as isSilentReplyPayloadText, c as startsWithSilentToken, i as isInternalFormattingArtifact, l as stripLeadingSilentToken, s as isSilentReplyText, u as stripSilentToken } from "./tokens-D4ghQG_g.mjs"; import { o as hasReplyPayloadContent } from "./payload-DfZ7JVGs.mjs"; import { E as setReplyPayloadMetadata, c as getReplyPayloadMetadata, o as copyReplyPayloadMetadata, u as hasReplyPayloadSpeechContent } from "./reply-payload-NzglYOOT.mjs"; import { u as stripHeartbeatToken } from "./heartbeat-BjLIgeQc.mjs"; import { A as sanitizeUserFacingText, n as renderUserFacingText } from "./user-facing-text-DC1sCo4O.mjs"; //#region src/auto-reply/reply/response-prefix-template.ts const TEMPLATE_VAR_PATTERN = /\{([a-zA-Z][a-zA-Z0-9.]*)\}/g; /** Variable names are case-insensitive; unresolved placeholders remain literal. */ function resolveResponsePrefixTemplate(template, context) { if (!template) return; return template.replace(TEMPLATE_VAR_PATTERN, (match, varName) => { switch (normalizeLowercaseStringOrEmpty(varName)) { case "model": return context.model ?? match; case "modelfull": return context.modelFull ?? match; case "provider": return context.provider ?? match; case "thinkinglevel": case "think": return context.thinkingLevel ?? match; case "identity.name": case "identityname": return context.identityName ?? match; default: return match; } }); } /** Strips the provider prefix, date suffix, and trailing -latest tag for display. */ function extractShortModelName(fullModel) { const slash = fullModel.lastIndexOf("/"); return (slash >= 0 ? fullModel.slice(slash + 1) : fullModel).replace(/-\d{8}$/, "").replace(/-latest$/, ""); } //#endregion //#region src/auto-reply/reply/normalize-reply.ts const channelReplyTransformOwners = /* @__PURE__ */ new WeakMap(); function bindNormalizeReplyTransformOwner(transform, owner) { channelReplyTransformOwners.set(transform, owner); return transform; } function resolveNormalizeReplyTransformOwner(transform) { return transform ? channelReplyTransformOwners.get(transform) ?? transform : void 0; } function normalizeReplyPayloadOutcome(payload, opts = {}) { const suppress = (reason) => { opts.onSkip?.(reason); return { kind: "suppress", reason }; }; const applyChannelTransforms = opts.applyChannelTransforms ?? true; const hasContent = (text) => hasReplyPayloadContent({ ...payload, text }, { trimText: true }); const trimmed = normalizeOptionalString(payload.text) ?? ""; const hasSpeechContent = hasReplyPayloadSpeechContent(payload); if (!hasContent(trimmed) && !hasSpeechContent) return suppress("empty"); let text = payload.text ?? void 0; if (!getReplyPayloadMetadata(payload)?.heartbeatReply) { const silentToken = opts.silentToken ?? "NO_REPLY"; if (text && isSilentReplyPayloadText(text, silentToken)) { if (!hasContent("")) return suppress("silent"); text = ""; } if (text && !isSilentReplyText(text, silentToken)) { const hasLeadingSilentToken = startsWithSilentToken(text, silentToken); if (hasLeadingSilentToken) text = stripLeadingSilentToken(text, silentToken); if (hasLeadingSilentToken || text.toLowerCase().includes(silentToken.toLowerCase())) { text = stripSilentToken(text, silentToken); if (!hasContent(text)) return suppress("silent"); } } if (text && !trimmed) text = ""; if (text?.includes("HEARTBEAT_OK")) { const stripped = stripHeartbeatToken(text, { mode: "message" }); if (stripped.didStrip) opts.onHeartbeatStrip?.(); if (stripped.shouldSkip && !hasContent(stripped.text)) return suppress("heartbeat"); text = stripped.text; } if (text && isInternalFormattingArtifact(text) && !hasContent("")) return suppress("silent"); if (text) text = payload.isError ? renderUserFacingText(text, { errorContext: true, conversationContext: opts.conversationContext }) : sanitizeUserFacingText(text, { conversationContext: opts.conversationContext }); if (!hasContent(text) && !hasSpeechContent) return suppress("empty"); } let enrichedPayload = copyReplyPayloadMetadata(payload, { ...payload, text }); const channelTransformOwner = resolveNormalizeReplyTransformOwner(opts.transformReplyPayload); const transformAlreadyApplied = channelTransformOwner != null && getReplyPayloadMetadata(enrichedPayload)?.channelReplyTransformOwner === channelTransformOwner; if (applyChannelTransforms && opts.transformReplyPayload && !transformAlreadyApplied) { const transformedPayload = opts.transformReplyPayload(enrichedPayload); if (transformedPayload === null) return suppress("channel_transform"); const copiedPayload = transformedPayload ? copyReplyPayloadMetadata(enrichedPayload, transformedPayload) : enrichedPayload; const appliedOwner = resolveNormalizeReplyTransformOwner(opts.transformReplyPayload); enrichedPayload = appliedOwner ? setReplyPayloadMetadata(copiedPayload, { channelReplyTransformOwner: appliedOwner }) : copiedPayload; text = enrichedPayload.text; } const effectivePrefix = opts.responsePrefixContext ? resolveResponsePrefixTemplate(opts.responsePrefix, opts.responsePrefixContext) : opts.responsePrefix; if (effectivePrefix && text && text.trim() !== "HEARTBEAT_OK" && !text.startsWith(effectivePrefix)) text = `${effectivePrefix} ${text}`; enrichedPayload = copyReplyPayloadMetadata(enrichedPayload, { ...enrichedPayload, text }); return { kind: "deliver", payload: enrichedPayload }; } function normalizeReplyPayload(payload, opts = {}) { const outcome = normalizeReplyPayloadOutcome(payload, opts); return outcome.kind === "deliver" ? outcome.payload : null; } //#endregion export { resolveResponsePrefixTemplate as a, extractShortModelName as i, normalizeReplyPayload as n, normalizeReplyPayloadOutcome as r, bindNormalizeReplyTransformOwner as t };