openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
201 lines (200 loc) • 7.93 kB
JavaScript
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 };