mirror of
https://github.com/moltbot/moltbot.git
synced 2026-03-21 16:41:56 +00:00
perf(routing): cache route and mention regex resolution
This commit is contained in:
@@ -21,6 +21,8 @@ function deriveMentionPatterns(identity?: { name?: string; emoji?: string }) {
|
||||
}
|
||||
|
||||
const BACKSPACE_CHAR = "\u0008";
|
||||
const mentionRegexCompileCache = new Map<string, RegExp[]>();
|
||||
const MAX_MENTION_REGEX_COMPILE_CACHE_KEYS = 512;
|
||||
|
||||
export const CURRENT_MESSAGE_MARKER = "[Current message - respond to this]";
|
||||
|
||||
@@ -54,7 +56,15 @@ function resolveMentionPatterns(cfg: OpenClawConfig | undefined, agentId?: strin
|
||||
|
||||
export function buildMentionRegexes(cfg: OpenClawConfig | undefined, agentId?: string): RegExp[] {
|
||||
const patterns = normalizeMentionPatterns(resolveMentionPatterns(cfg, agentId));
|
||||
return patterns
|
||||
if (patterns.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const cacheKey = patterns.join("\u001f");
|
||||
const cached = mentionRegexCompileCache.get(cacheKey);
|
||||
if (cached) {
|
||||
return [...cached];
|
||||
}
|
||||
const compiled = patterns
|
||||
.map((pattern) => {
|
||||
try {
|
||||
return new RegExp(pattern, "i");
|
||||
@@ -63,6 +73,12 @@ export function buildMentionRegexes(cfg: OpenClawConfig | undefined, agentId?: s
|
||||
}
|
||||
})
|
||||
.filter((value): value is RegExp => Boolean(value));
|
||||
mentionRegexCompileCache.set(cacheKey, compiled);
|
||||
if (mentionRegexCompileCache.size > MAX_MENTION_REGEX_COMPILE_CACHE_KEYS) {
|
||||
mentionRegexCompileCache.clear();
|
||||
mentionRegexCompileCache.set(cacheKey, compiled);
|
||||
}
|
||||
return [...compiled];
|
||||
}
|
||||
|
||||
export function normalizeMentionText(text: string): string {
|
||||
|
||||
@@ -204,6 +204,16 @@ type EvaluatedBindingsCache = {
|
||||
|
||||
const evaluatedBindingsCacheByCfg = new WeakMap<OpenClawConfig, EvaluatedBindingsCache>();
|
||||
const MAX_EVALUATED_BINDINGS_CACHE_KEYS = 2000;
|
||||
const resolvedRouteCacheByCfg = new WeakMap<
|
||||
OpenClawConfig,
|
||||
{
|
||||
bindingsRef: OpenClawConfig["bindings"];
|
||||
agentsRef: OpenClawConfig["agents"];
|
||||
sessionRef: OpenClawConfig["session"];
|
||||
byKey: Map<string, ResolvedAgentRoute>;
|
||||
}
|
||||
>();
|
||||
const MAX_RESOLVED_ROUTE_CACHE_KEYS = 4000;
|
||||
|
||||
type EvaluatedBindingsIndex = {
|
||||
byPeer: Map<string, EvaluatedBinding[]>;
|
||||
@@ -411,6 +421,33 @@ function normalizeBindingMatch(
|
||||
};
|
||||
}
|
||||
|
||||
function resolveRouteCacheForConfig(cfg: OpenClawConfig): Map<string, ResolvedAgentRoute> {
|
||||
const existing = resolvedRouteCacheByCfg.get(cfg);
|
||||
if (
|
||||
existing &&
|
||||
existing.bindingsRef === cfg.bindings &&
|
||||
existing.agentsRef === cfg.agents &&
|
||||
existing.sessionRef === cfg.session
|
||||
) {
|
||||
return existing.byKey;
|
||||
}
|
||||
const byKey = new Map<string, ResolvedAgentRoute>();
|
||||
resolvedRouteCacheByCfg.set(cfg, {
|
||||
bindingsRef: cfg.bindings,
|
||||
agentsRef: cfg.agents,
|
||||
sessionRef: cfg.session,
|
||||
byKey,
|
||||
});
|
||||
return byKey;
|
||||
}
|
||||
|
||||
function formatRouteCachePeer(peer: RoutePeer | null): string {
|
||||
if (!peer || !peer.id) {
|
||||
return "-";
|
||||
}
|
||||
return `${peer.kind}:${peer.id}`;
|
||||
}
|
||||
|
||||
function hasGuildConstraint(match: NormalizedBindingMatch): boolean {
|
||||
return Boolean(match.guildId);
|
||||
}
|
||||
@@ -474,13 +511,40 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
|
||||
const teamId = normalizeId(input.teamId);
|
||||
const memberRoleIds = input.memberRoleIds ?? [];
|
||||
const memberRoleIdSet = new Set(memberRoleIds);
|
||||
const dmScope = input.cfg.session?.dmScope ?? "main";
|
||||
const identityLinks = input.cfg.session?.identityLinks;
|
||||
const shouldLogDebug = shouldLogVerbose();
|
||||
const parentPeer = input.parentPeer
|
||||
? {
|
||||
kind: normalizeChatType(input.parentPeer.kind) ?? input.parentPeer.kind,
|
||||
id: normalizeId(input.parentPeer.id),
|
||||
}
|
||||
: null;
|
||||
|
||||
const routeCache =
|
||||
!shouldLogDebug && !identityLinks ? resolveRouteCacheForConfig(input.cfg) : null;
|
||||
const routeCacheKey = routeCache
|
||||
? [
|
||||
channel,
|
||||
accountId,
|
||||
formatRouteCachePeer(peer),
|
||||
formatRouteCachePeer(parentPeer),
|
||||
guildId || "-",
|
||||
teamId || "-",
|
||||
memberRoleIds.length > 0 ? memberRoleIds.toSorted().join(",") : "-",
|
||||
dmScope,
|
||||
].join("\t")
|
||||
: "";
|
||||
if (routeCache && routeCacheKey) {
|
||||
const cachedRoute = routeCache.get(routeCacheKey);
|
||||
if (cachedRoute) {
|
||||
return { ...cachedRoute };
|
||||
}
|
||||
}
|
||||
|
||||
const bindings = getEvaluatedBindingsForChannelAccount(input.cfg, channel, accountId);
|
||||
const bindingsIndex = getEvaluatedBindingIndexForChannelAccount(input.cfg, channel, accountId);
|
||||
|
||||
const dmScope = input.cfg.session?.dmScope ?? "main";
|
||||
const identityLinks = input.cfg.session?.identityLinks;
|
||||
|
||||
const choose = (agentId: string, matchedBy: ResolvedAgentRoute["matchedBy"]) => {
|
||||
const resolvedAgentId = pickFirstExistingAgentId(input.cfg, agentId);
|
||||
const sessionKey = buildAgentSessionKey({
|
||||
@@ -495,7 +559,7 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
|
||||
agentId: resolvedAgentId,
|
||||
mainKey: DEFAULT_MAIN_KEY,
|
||||
}).toLowerCase();
|
||||
return {
|
||||
const route = {
|
||||
agentId: resolvedAgentId,
|
||||
channel,
|
||||
accountId,
|
||||
@@ -503,9 +567,16 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
|
||||
mainSessionKey,
|
||||
matchedBy,
|
||||
};
|
||||
if (routeCache && routeCacheKey) {
|
||||
routeCache.set(routeCacheKey, route);
|
||||
if (routeCache.size > MAX_RESOLVED_ROUTE_CACHE_KEYS) {
|
||||
routeCache.clear();
|
||||
routeCache.set(routeCacheKey, route);
|
||||
}
|
||||
}
|
||||
return route;
|
||||
};
|
||||
|
||||
const shouldLogDebug = shouldLogVerbose();
|
||||
const formatPeer = (value?: RoutePeer | null) =>
|
||||
value?.kind && value?.id ? `${value.kind}:${value.id}` : "none";
|
||||
const formatNormalizedPeer = (value: NormalizedPeerConstraint) => {
|
||||
@@ -529,12 +600,6 @@ export function resolveAgentRoute(input: ResolveAgentRouteInput): ResolvedAgentR
|
||||
}
|
||||
}
|
||||
// Thread parent inheritance: if peer (thread) didn't match, check parent peer binding
|
||||
const parentPeer = input.parentPeer
|
||||
? {
|
||||
kind: normalizeChatType(input.parentPeer.kind) ?? input.parentPeer.kind,
|
||||
id: normalizeId(input.parentPeer.id),
|
||||
}
|
||||
: null;
|
||||
const baseScope = {
|
||||
guildId,
|
||||
teamId,
|
||||
|
||||
Reference in New Issue
Block a user