UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

437 lines (436 loc) 15.9 kB
import { i as formatErrorMessage } from "./errors-BXgSefBE.js"; import { n as resolveGlobalSingleton } from "./global-singleton-PwlQSEal.js"; import { t as createSubsystemLogger } from "./subsystem-BzXSmsuh.js"; import { u as normalizeAgentId } from "./session-key-B_NoIfpX.js"; import { i as resolveAgentContextLimits, o as resolveAgentWorkspaceDir } from "./agent-scope-config-CgCYpZfK.js"; import "./error-runtime-C8vbtAJt.js"; import { n as resolveMemorySearchSyncConfig } from "./memory-search-CTXTHmj4.js"; import "./routing-CQ63ICPX.js"; import "./memory-core-host-engine-foundation-_JoMi6_G.js"; import { r as resolveQmdBinaryUnavailableReason, t as checkQmdBinaryAvailability } from "./engine-qmd-BXdBKAnj.js"; import "./memory-core-host-engine-qmd-C4kBQq_B.js"; import { t as resolveMemoryBackendConfig } from "./backend-config-Bk4OTHnB.js"; import "./memory-core-host-engine-storage-Ngip6PSU.js"; import "./manager-Bs7MKMfS.js"; import fs from "node:fs/promises"; //#region extensions/memory-core/src/memory/search-manager.ts const MEMORY_SEARCH_MANAGER_CACHE_KEY = Symbol.for("openclaw.memorySearchManagerCache"); const QMD_MANAGER_OPEN_FAILURE_COOLDOWN_MS = 6e4; function createMemorySearchManagerCacheStore() { return { qmdManagerCache: /* @__PURE__ */ new Map(), pendingQmdManagerCreates: /* @__PURE__ */ new Map(), qmdManagerOpenFailures: /* @__PURE__ */ new Map() }; } function getMemorySearchManagerCacheStore() { const resolved = resolveGlobalSingleton(MEMORY_SEARCH_MANAGER_CACHE_KEY, createMemorySearchManagerCacheStore); if (typeof resolved === "object" && resolved !== null && resolved.qmdManagerCache instanceof Map && resolved.pendingQmdManagerCreates instanceof Map) { const cacheStore = resolved; if (!(cacheStore.qmdManagerOpenFailures instanceof Map)) cacheStore.qmdManagerOpenFailures = /* @__PURE__ */ new Map(); return cacheStore; } const repaired = createMemorySearchManagerCacheStore(); globalThis[MEMORY_SEARCH_MANAGER_CACHE_KEY] = repaired; return repaired; } const log = createSubsystemLogger("memory"); const { qmdManagerCache: QMD_MANAGER_CACHE, pendingQmdManagerCreates: PENDING_QMD_MANAGER_CREATES, qmdManagerOpenFailures: QMD_MANAGER_OPEN_FAILURES } = getMemorySearchManagerCacheStore(); let managerRuntimePromise = null; let qmdManagerModulePromise = null; function loadManagerRuntime() { managerRuntimePromise ??= import("./extensions/memory-core/manager-runtime.js"); return managerRuntimePromise; } function loadQmdManagerModule() { qmdManagerModulePromise ??= import("./qmd-manager-CX2gigDl.js"); return qmdManagerModulePromise; } function getActiveQmdManagerOpenFailure(scopeKey, identityKey, nowMs = Date.now()) { const failure = QMD_MANAGER_OPEN_FAILURES.get(scopeKey); if (!failure) return null; if (failure.identityKey !== identityKey || failure.retryAfterMs <= nowMs) { QMD_MANAGER_OPEN_FAILURES.delete(scopeKey); return null; } return failure; } function recordQmdManagerOpenFailure(scopeKey, identityKey, reason, nowMs = Date.now()) { QMD_MANAGER_OPEN_FAILURES.set(scopeKey, { identityKey, reason, retryAfterMs: nowMs + QMD_MANAGER_OPEN_FAILURE_COOLDOWN_MS }); } function clearQmdManagerOpenFailure(scopeKey, identityKey) { if (QMD_MANAGER_OPEN_FAILURES.get(scopeKey)?.identityKey === identityKey) QMD_MANAGER_OPEN_FAILURES.delete(scopeKey); } async function getMemorySearchManager(params) { const resolved = resolveMemoryBackendConfig(params); if (resolved.backend === "qmd" && resolved.qmd) { const qmdResolved = resolved.qmd; const normalizedAgentId = normalizeAgentId(params.agentId); const runtimeConfig = resolveQmdManagerRuntimeConfig(params.cfg, normalizedAgentId); const { workspaceDir } = runtimeConfig; const transient = params.purpose === "status" || params.purpose === "cli"; const scopeKey = buildQmdManagerScopeKey(normalizedAgentId); const identityKey = buildQmdManagerIdentityKey(normalizedAgentId, qmdResolved, runtimeConfig); const createPrimaryQmdManager = async (mode) => { try { await fs.mkdir(workspaceDir, { recursive: true }); } catch (err) { const message = formatErrorMessage(err); log.warn(`qmd workspace unavailable (${workspaceDir}); falling back to builtin: ${message}`); return { manager: null, failureReason: `qmd workspace unavailable (${workspaceDir}): ${message}` }; } const qmdBinary = await checkQmdBinaryAvailability({ command: qmdResolved.command, env: process.env, cwd: workspaceDir }); if (!qmdBinary.available) { const message = qmdBinary.error; const failurePrefix = resolveQmdBinaryUnavailableReason(qmdBinary) === "workspace-cwd" ? `qmd workspace unavailable (${workspaceDir})` : `qmd binary unavailable (${qmdResolved.command})`; log.warn(`${failurePrefix}; falling back to builtin: ${message}`); return { manager: null, failureReason: `${failurePrefix}: ${message}` }; } try { const { QmdMemoryManager } = await loadQmdManagerModule(); const primary = await QmdMemoryManager.create({ cfg: params.cfg, agentId: normalizedAgentId, resolved: { ...resolved, qmd: qmdResolved }, mode, runtimeConfig }); if (primary) { clearQmdManagerOpenFailure(scopeKey, identityKey); return { manager: primary }; } } catch (err) { const message = formatErrorMessage(err); log.warn(`qmd memory unavailable; falling back to builtin: ${message}`); return { manager: null, failureReason: `qmd memory unavailable: ${message}` }; } return { manager: null, failureReason: "qmd memory unavailable: no manager returned" }; }; const createFullQmdManager = async (expectedIdentityKey) => { const { manager: primary, failureReason } = await createPrimaryQmdManager("full"); if (!primary) return { entry: null, failureReason }; const cacheEntry = { identityKey: expectedIdentityKey, manager: new FallbackMemoryManager({ primary, fallbackFactory: async () => { const { MemoryIndexManager } = await loadManagerRuntime(); return await MemoryIndexManager.get(params); } }, () => { if (QMD_MANAGER_CACHE.get(scopeKey) === cacheEntry) QMD_MANAGER_CACHE.delete(scopeKey); }) }; return { entry: cacheEntry }; }; const cached = QMD_MANAGER_CACHE.get(scopeKey); if (cached?.identityKey === identityKey) { if (params.purpose === "status") return { manager: new BorrowedMemoryManager(cached.manager) }; if (params.purpose !== "cli") return { manager: cached.manager }; } if (transient) { const { manager } = await createPrimaryQmdManager(params.purpose === "cli" ? "cli" : "status"); return manager ? { manager } : await getBuiltinMemorySearchManager(params); } const recentFailure = getActiveQmdManagerOpenFailure(scopeKey, identityKey); if (recentFailure) { log.debug?.(`qmd memory unavailable; using builtin during cooldown: ${recentFailure.reason}`); return await getBuiltinMemorySearchManager(params); } const pending = PENDING_QMD_MANAGER_CREATES.get(scopeKey); if (pending) { await pending.promise; return await getMemorySearchManager(params); } const pendingCreate = { identityKey, promise: (async () => { const created = await createFullQmdManager(identityKey); if (!created.entry) { recordQmdManagerOpenFailure(scopeKey, identityKey, created.failureReason ?? "qmd memory unavailable"); return null; } QMD_MANAGER_CACHE.set(scopeKey, created.entry); if (cached) await closeQmdManagerForReplacement(cached.manager).catch((err) => { log.warn(`failed to retire replaced qmd memory manager: ${formatErrorMessage(err)}`); }); return created.entry.manager; })().finally(() => { if (PENDING_QMD_MANAGER_CREATES.get(scopeKey) === pendingCreate) PENDING_QMD_MANAGER_CREATES.delete(scopeKey); }) }; PENDING_QMD_MANAGER_CREATES.set(scopeKey, pendingCreate); const manager = await pendingCreate.promise; return manager ? { manager } : await getBuiltinMemorySearchManager(params); } return await getBuiltinMemorySearchManager(params); } async function getBuiltinMemorySearchManager(params) { try { const { MemoryIndexManager } = await loadManagerRuntime(); return { manager: await MemoryIndexManager.get(params) }; } catch (err) { return { manager: null, error: formatErrorMessage(err) }; } } var BorrowedMemoryManager = class { constructor(inner) { this.inner = inner; if (inner.probeVectorStoreAvailability) { const probeVectorStoreAvailability = inner.probeVectorStoreAvailability.bind(inner); this.probeVectorStoreAvailability = async () => await probeVectorStoreAvailability(); } } async search(query, opts) { return await this.inner.search(query, opts); } async readFile(params) { return await this.inner.readFile(params); } status() { return this.inner.status(); } async sync(params) { await this.inner.sync?.(params); } async probeEmbeddingAvailability() { return await this.inner.probeEmbeddingAvailability(); } getCachedEmbeddingAvailability() { return this.inner.getCachedEmbeddingAvailability?.() ?? null; } async probeVectorAvailability() { return await this.inner.probeVectorAvailability(); } async close() {} }; async function closeAllMemorySearchManagers() { const pendingCreates = Array.from(PENDING_QMD_MANAGER_CREATES.values(), (entry) => entry.promise); await Promise.allSettled(pendingCreates); const managers = Array.from(QMD_MANAGER_CACHE.values(), (entry) => entry.manager); PENDING_QMD_MANAGER_CREATES.clear(); QMD_MANAGER_CACHE.clear(); QMD_MANAGER_OPEN_FAILURES.clear(); for (const manager of managers) try { await manager.close?.(); } catch (err) { log.warn(`failed to close qmd memory manager: ${String(err)}`); } if (managerRuntimePromise !== null) { const { closeAllMemoryIndexManagers } = await loadManagerRuntime(); await closeAllMemoryIndexManagers(); } } async function closeMemorySearchManager(params) { const normalizedAgentId = normalizeAgentId(params.agentId); const scopeKey = buildQmdManagerScopeKey(normalizedAgentId); const pending = PENDING_QMD_MANAGER_CREATES.get(scopeKey); if (pending) await Promise.allSettled([pending.promise]); const cached = QMD_MANAGER_CACHE.get(scopeKey); if (cached) { QMD_MANAGER_CACHE.delete(scopeKey); QMD_MANAGER_OPEN_FAILURES.delete(scopeKey); try { await cached.manager.close?.(); } catch (err) { log.warn(`failed to close qmd memory manager for agent ${normalizedAgentId}: ${String(err)}`); } } if (managerRuntimePromise !== null) { const { closeMemoryIndexManagersForAgent } = await loadManagerRuntime(); await closeMemoryIndexManagersForAgent({ cfg: params.cfg, agentId: normalizedAgentId }); } } var FallbackMemoryManager = class { constructor(deps, onClose) { this.deps = deps; this.onClose = onClose; this.fallback = null; this.primaryFailed = false; this.cacheEvicted = false; this.closed = false; this.closeReason = "memory search manager is closed"; } async search(query, opts) { this.ensureOpen(); if (!this.primaryFailed) try { return await this.deps.primary.search(query, opts); } catch (err) { this.primaryFailed = true; this.lastError = formatErrorMessage(err); log.warn(`qmd memory failed; switching to builtin index: ${this.lastError}`); await this.deps.primary.close?.().catch(() => {}); this.evictCacheEntry(); } const fallback = await this.ensureFallback(); if (fallback) return await fallback.search(query, opts); throw new Error(this.lastError ?? "memory search unavailable"); } async readFile(params) { this.ensureOpen(); if (!this.primaryFailed) return await this.deps.primary.readFile(params); const fallback = await this.ensureFallback(); if (fallback) return await fallback.readFile(params); throw new Error(this.lastError ?? "memory read unavailable"); } status() { this.ensureOpen(); if (!this.primaryFailed) return this.deps.primary.status(); const fallbackStatus = this.fallback?.status(); const fallbackInfo = { from: "qmd", reason: this.lastError ?? "unknown" }; if (fallbackStatus) { const custom = fallbackStatus.custom ?? {}; return { ...fallbackStatus, fallback: fallbackInfo, custom: { ...custom, fallback: { disabled: true, reason: this.lastError ?? "unknown" } } }; } const primaryStatus = this.deps.primary.status(); const custom = primaryStatus.custom ?? {}; return { ...primaryStatus, fallback: fallbackInfo, custom: { ...custom, fallback: { disabled: true, reason: this.lastError ?? "unknown" } } }; } async sync(params) { this.ensureOpen(); if (!this.primaryFailed) { await this.deps.primary.sync?.(params); return; } await (await this.ensureFallback())?.sync?.(params); } async probeEmbeddingAvailability() { this.ensureOpen(); if (!this.primaryFailed) return await this.deps.primary.probeEmbeddingAvailability(); const fallback = await this.ensureFallback(); if (fallback) return await fallback.probeEmbeddingAvailability(); return { ok: false, error: this.lastError ?? "memory embeddings unavailable" }; } getCachedEmbeddingAvailability() { this.ensureOpen(); if (!this.primaryFailed) return this.deps.primary.getCachedEmbeddingAvailability?.() ?? null; return this.fallback?.getCachedEmbeddingAvailability?.() ?? null; } async probeVectorStoreAvailability() { this.ensureOpen(); if (!this.primaryFailed) return await (this.deps.primary.probeVectorStoreAvailability?.() ?? this.deps.primary.probeVectorAvailability()); const fallback = await this.ensureFallback(); return await (fallback?.probeVectorStoreAvailability?.() ?? fallback?.probeVectorAvailability()) ?? false; } async probeVectorAvailability() { this.ensureOpen(); if (!this.primaryFailed) return await this.deps.primary.probeVectorAvailability(); return await (await this.ensureFallback())?.probeVectorAvailability() ?? false; } async close() { if (this.closed) return; this.closed = true; await this.deps.primary.close?.(); await this.fallback?.close?.(); this.evictCacheEntry(); } async invalidate(reason) { this.closeReason = reason; await this.close(); } async ensureFallback() { if (this.fallback) return this.fallback; let fallback; try { fallback = await this.deps.fallbackFactory(); if (!fallback) { log.warn("memory fallback requested but builtin index is unavailable"); return null; } } catch (err) { const message = formatErrorMessage(err); log.warn(`memory fallback unavailable: ${message}`); return null; } this.fallback = fallback; return this.fallback; } ensureOpen() { if (this.closed) throw new Error(this.closeReason); } evictCacheEntry() { if (this.cacheEvicted) return; this.cacheEvicted = true; this.onClose?.(); } }; async function closeQmdManagerForReplacement(manager) { if (manager instanceof FallbackMemoryManager) { await manager.invalidate("memory search manager was replaced by a newer qmd manager"); return; } await manager.close?.(); } function buildQmdManagerScopeKey(agentId) { return agentId; } function buildQmdManagerIdentityKey(agentId, config, runtimeConfig) { return `${agentId}:${JSON.stringify(config)}:${JSON.stringify(runtimeConfig.syncSettings ?? null)}:${JSON.stringify(runtimeConfig.contextLimits ?? null)}:${runtimeConfig.workspaceDir}`; } function resolveQmdManagerRuntimeConfig(cfg, agentId) { return { workspaceDir: resolveAgentWorkspaceDir(cfg, agentId), syncSettings: resolveMemorySearchSyncConfig(cfg, agentId), contextLimits: resolveAgentContextLimits(cfg, agentId) }; } //#endregion export { closeMemorySearchManager as n, getMemorySearchManager as r, closeAllMemorySearchManagers as t };