UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

201 lines (200 loc) 7.93 kB
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js"; import { r as DEFAULT_PROVIDER } from "./defaults-CdX9UGcX.js"; import { o as resolveAgentEffectiveModelPrimary } from "./agent-scope-DbtJyKUL.js"; import { n as parseModelRef } from "./model-selection-normalize-D1HuPOqZ.js"; import { t as resolveDefaultModelForAgent } from "./model-selection-config-BrdmmqKD.js"; import "./agent-runtime-B15RhSG0.js"; import "./text-utility-runtime-BjzvUG99.js"; import { B as RECALLED_CONTEXT_LINE_PATTERNS, a as ACTIVE_MEMORY_OPEN_TAG, n as ACTIVE_MEMORY_CLOSE_TAG } from "./types-C1PF5JnV.js"; //#region extensions/active-memory/query.ts function buildQuery(params) { const latest = params.latestUserMessage.trim(); if (params.config.queryMode === "message") return latest; if (params.config.queryMode === "full") { const allTurns = (params.recentTurns ?? []).map((turn) => `${turn.role}: ${turn.text.trim().replace(/\s+/g, " ")}`).filter((turn) => turn.length > 0); if (allTurns.length === 0) return latest; return [ "Full conversation context:", ...allTurns, "", "Latest user message:", latest ].join("\n"); } let remainingUser = params.config.recentUserTurns; let remainingAssistant = params.config.recentAssistantTurns; const selected = []; for (let index = (params.recentTurns ?? []).length - 1; index >= 0; index -= 1) { const turn = params.recentTurns?.[index]; if (!turn) continue; if (turn.role === "user") { if (remainingUser <= 0) continue; remainingUser -= 1; selected.push({ role: "user", text: truncateUtf16Safe(turn.text.trim().replace(/\s+/g, " "), params.config.recentUserChars) }); continue; } if (remainingAssistant <= 0) continue; remainingAssistant -= 1; selected.push({ role: "assistant", text: truncateUtf16Safe(turn.text.trim().replace(/\s+/g, " "), params.config.recentAssistantChars) }); } const recentTurns = selected.toReversed().filter((turn) => turn.text.length > 0); if (recentTurns.length === 0) return latest; return [ "Recent conversation tail:", ...recentTurns.map((turn) => `${turn.role}: ${turn.text}`), "", "Latest user message:", latest ].join("\n"); } function stripExternalUntrustedBlocks(text) { return text.replace(/<<<EXTERNAL_UNTRUSTED_CONTENT\b[^>]*>>>[\s\S]*?<<<END_EXTERNAL_UNTRUSTED_CONTENT\b[^>]*>>>/g, " "); } function stripJsonFences(text) { return text.replace(/```(?:json)?\s*[\s\S]*?```/gi, " "); } function stripActiveMemoryXmlBlocks(text) { return text.replace(/<active_memory_plugin>[\s\S]*?<\/active_memory_plugin>/gi, " "); } function normalizeSearchQueryText(text) { return text.split("\n").map((line) => line.trim()).filter((line) => { if (!line) return false; if (line === "Context:") return false; if (/^(conversation info|sender|untrusted context)\b/i.test(line)) return false; if (/^(source: external|---|untrusted discord message body)$/i.test(line)) return false; if (/^⚠️?\s*Agent couldn't generate a response/i.test(line)) return false; if (/^Please try again\.?$/i.test(line)) return false; return true; }).join(" ").replace(/\s+/g, " ").trim(); } function clampSearchQuery(text) { const normalized = text.replace(/\s+/g, " ").trim(); return normalized.length > 480 ? truncateUtf16Safe(normalized, 480).trim() : normalized; } function buildSearchQuery(params) { const latest = clampSearchQuery(normalizeSearchQueryText(stripActiveMemoryXmlBlocks(stripJsonFences(stripExternalUntrustedBlocks(params.latestUserMessage))))); if (latest.length >= 12 || !params.recentTurns?.length) return latest || clampSearchQuery(params.latestUserMessage); const previousUser = [...params.recentTurns].toReversed().find((turn) => turn.role === "user" && turn.text.trim() !== params.latestUserMessage.trim()); if (!previousUser) return latest || clampSearchQuery(params.latestUserMessage); const context = truncateUtf16Safe(clampSearchQuery(normalizeSearchQueryText(stripRecalledContextNoise(previousUser.text))), 120).trim(); return clampSearchQuery(context ? `${context} ${latest}` : latest); } function extractTextContentParts(content) { if (typeof content === "string") return content.trim() ? [content] : []; if (!Array.isArray(content)) return []; const parts = []; for (const item of content) { if (typeof item === "string") { parts.push(item); continue; } if (!item || typeof item !== "object") continue; const typed = item; if (typeof typed.text === "string") { parts.push(typed.text); continue; } if (typed.type === "text" && typeof typed.content === "string") parts.push(typed.content); } return parts.map((part) => part.trim()).filter(Boolean); } function extractTextContent(content) { return extractTextContentParts(content).join(" ").trim(); } function stripRecalledContextNoise(text) { const lines = text.split("\n"); const cleanedLines = []; for (let index = 0; index < lines.length; index += 1) { const line = lines[index]?.trim() ?? ""; if (!line) continue; if (line === "Context:") continue; if (line === ACTIVE_MEMORY_OPEN_TAG) { let closeIndex = -1; for (let probe = index + 1; probe < lines.length; probe += 1) if ((lines[probe]?.trim() ?? "") === ACTIVE_MEMORY_CLOSE_TAG) { closeIndex = probe; break; } if (closeIndex !== -1) { index = closeIndex; continue; } } if (line === ACTIVE_MEMORY_CLOSE_TAG) continue; if (RECALLED_CONTEXT_LINE_PATTERNS.some((pattern) => pattern.test(line))) continue; cleanedLines.push(line); } return cleanedLines.join(" ").replace(/\s+/g, " ").trim(); } function stripInjectedActiveMemoryPrefixOnly(text) { const lines = text.split("\n"); const cleanedLines = []; for (let index = 0; index < lines.length; index += 1) { const line = lines[index]?.trim() ?? ""; if (!line) continue; if (line === "Context:") { if ((lines[index + 1]?.trim() ?? "") === ACTIVE_MEMORY_OPEN_TAG) { let closeIndex = -1; for (let probe = index + 2; probe < lines.length; probe += 1) if ((lines[probe]?.trim() ?? "") === ACTIVE_MEMORY_CLOSE_TAG) { closeIndex = probe; break; } if (closeIndex !== -1) { index = closeIndex; continue; } } } cleanedLines.push(line); } return cleanedLines.join(" ").replace(/\s+/g, " ").trim(); } function extractRecentTurns(messages) { const turns = []; for (const message of messages) { if (!message || typeof message !== "object") continue; const typed = message; const role = typed.role === "user" || typed.role === "assistant" ? typed.role : void 0; if (!role) continue; const rawText = extractTextContent(typed.content); const text = role === "assistant" ? stripRecalledContextNoise(rawText) : stripInjectedActiveMemoryPrefixOnly(rawText); if (!text) continue; turns.push({ role, text }); } return turns; } function parseModelCandidate(modelRef, defaultProvider = DEFAULT_PROVIDER) { if (!modelRef) return; return parseModelRef(modelRef, defaultProvider) ?? { provider: defaultProvider, model: modelRef }; } function getModelRef(runtimeConfig, agentId, config, ctx) { const currentRunModel = ctx?.modelProviderId && ctx?.modelId ? `${ctx.modelProviderId}/${ctx.modelId}` : void 0; const configuredDefaultModel = resolveAgentEffectiveModelPrimary(runtimeConfig, agentId) ? resolveDefaultModelForAgent({ cfg: runtimeConfig, agentId }) : void 0; const defaultProvider = configuredDefaultModel?.provider ?? "openai"; const candidates = [ config.model, currentRunModel, configuredDefaultModel ? `${configuredDefaultModel.provider}/${configuredDefaultModel.model}` : void 0, config.modelFallback ]; for (const candidate of candidates) { const parsed = parseModelCandidate(candidate, defaultProvider); if (parsed) return parsed; } } //#endregion export { extractTextContentParts as a, extractTextContent as i, buildSearchQuery as n, getModelRef as o, extractRecentTurns as r, buildQuery as t };