UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

215 lines (214 loc) 6.98 kB
//#region src/gateway/server-chat-state.ts /** Create the FIFO registry that maps session IDs to active chat runs. */ function createChatRunRegistry() { const chatRunSessions = /* @__PURE__ */ new Map(); const add = (sessionId, entry) => { const queue = chatRunSessions.get(sessionId); if (queue) queue.push(entry); else chatRunSessions.set(sessionId, [entry]); }; const peek = (sessionId) => chatRunSessions.get(sessionId)?.[0]; const shift = (sessionId) => { const queue = chatRunSessions.get(sessionId); if (!queue || queue.length === 0) return; const entry = queue.shift(); if (!queue.length) chatRunSessions.delete(sessionId); return entry; }; const remove = (sessionId, clientRunId, sessionKey) => { const queue = chatRunSessions.get(sessionId); if (!queue || queue.length === 0) return; const idx = queue.findIndex((entry) => entry.clientRunId === clientRunId && (sessionKey ? entry.sessionKey === sessionKey : true)); if (idx < 0) return; const [entry] = queue.splice(idx, 1); if (!queue.length) chatRunSessions.delete(sessionId); return entry; }; const clear = () => { chatRunSessions.clear(); }; return { add, peek, shift, remove, clear }; } /** Create all mutable chat-run maps used by Gateway runtime state. */ function createChatRunState() { const registry = createChatRunRegistry(); const rawBuffers = /* @__PURE__ */ new Map(); const buffers = /* @__PURE__ */ new Map(); const bufferUpdatedAt = /* @__PURE__ */ new Map(); const deltaSentAt = /* @__PURE__ */ new Map(); const deltaLastBroadcastLen = /* @__PURE__ */ new Map(); const deltaLastBroadcastText = /* @__PURE__ */ new Map(); const agentDeltaSentAt = /* @__PURE__ */ new Map(); const bufferedAgentEvents = /* @__PURE__ */ new Map(); const abortedRuns = /* @__PURE__ */ new Map(); const clearRun = (runId) => { rawBuffers.delete(runId); buffers.delete(runId); bufferUpdatedAt.delete(runId); deltaSentAt.delete(runId); deltaLastBroadcastLen.delete(runId); deltaLastBroadcastText.delete(runId); for (const key of [ runId, `${runId}:assistant`, `${runId}:thinking` ]) { agentDeltaSentAt.delete(key); bufferedAgentEvents.delete(key); } }; const clear = () => { registry.clear(); rawBuffers.clear(); buffers.clear(); bufferUpdatedAt.clear(); deltaSentAt.clear(); deltaLastBroadcastLen.clear(); deltaLastBroadcastText.clear(); agentDeltaSentAt.clear(); bufferedAgentEvents.clear(); abortedRuns.clear(); }; return { registry, rawBuffers, buffers, bufferUpdatedAt, deltaSentAt, deltaLastBroadcastLen, deltaLastBroadcastText, agentDeltaSentAt, bufferedAgentEvents, abortedRuns, clearRun, clear }; } const TOOL_EVENT_RECIPIENT_TTL_MS = 600 * 1e3; const TOOL_EVENT_RECIPIENT_FINAL_GRACE_MS = 30 * 1e3; /** Create the broad sessions.changed subscriber registry. */ function createSessionEventSubscriberRegistry() { const connIds = /* @__PURE__ */ new Set(); const empty = /* @__PURE__ */ new Set(); return { subscribe: (connId) => { const normalized = connId.trim(); if (!normalized) return; connIds.add(normalized); }, unsubscribe: (connId) => { const normalized = connId.trim(); if (!normalized) return; connIds.delete(normalized); }, getAll: () => connIds.size > 0 ? connIds : empty, clear: () => { connIds.clear(); } }; } /** Create the per-session message subscriber registry. */ function createSessionMessageSubscriberRegistry() { const sessionToConnIds = /* @__PURE__ */ new Map(); const connToSessionKeys = /* @__PURE__ */ new Map(); const empty = /* @__PURE__ */ new Set(); const normalize = (value) => value.trim(); return { subscribe: (connId, sessionKey) => { const normalizedConnId = normalize(connId); const normalizedSessionKey = normalize(sessionKey); if (!normalizedConnId || !normalizedSessionKey) return; const connIds = sessionToConnIds.get(normalizedSessionKey) ?? /* @__PURE__ */ new Set(); connIds.add(normalizedConnId); sessionToConnIds.set(normalizedSessionKey, connIds); const sessionKeys = connToSessionKeys.get(normalizedConnId) ?? /* @__PURE__ */ new Set(); sessionKeys.add(normalizedSessionKey); connToSessionKeys.set(normalizedConnId, sessionKeys); }, unsubscribe: (connId, sessionKey) => { const normalizedConnId = normalize(connId); const normalizedSessionKey = normalize(sessionKey); if (!normalizedConnId || !normalizedSessionKey) return; const connIds = sessionToConnIds.get(normalizedSessionKey); if (connIds) { connIds.delete(normalizedConnId); if (connIds.size === 0) sessionToConnIds.delete(normalizedSessionKey); } const sessionKeys = connToSessionKeys.get(normalizedConnId); if (sessionKeys) { sessionKeys.delete(normalizedSessionKey); if (sessionKeys.size === 0) connToSessionKeys.delete(normalizedConnId); } }, unsubscribeAll: (connId) => { const normalizedConnId = normalize(connId); if (!normalizedConnId) return; const sessionKeys = connToSessionKeys.get(normalizedConnId); if (!sessionKeys) return; for (const sessionKey of sessionKeys) { const connIds = sessionToConnIds.get(sessionKey); if (!connIds) continue; connIds.delete(normalizedConnId); if (connIds.size === 0) sessionToConnIds.delete(sessionKey); } connToSessionKeys.delete(normalizedConnId); }, get: (sessionKey) => { const normalizedSessionKey = normalize(sessionKey); if (!normalizedSessionKey) return empty; return sessionToConnIds.get(normalizedSessionKey) ?? empty; }, clear: () => { sessionToConnIds.clear(); connToSessionKeys.clear(); } }; } /** Create the run-id recipient registry used for streaming tool events. */ function createToolEventRecipientRegistry() { const recipients = /* @__PURE__ */ new Map(); const prune = () => { if (recipients.size === 0) return; const now = Date.now(); for (const [runId, entry] of recipients) if (now >= (entry.finalizedAt ? entry.finalizedAt + TOOL_EVENT_RECIPIENT_FINAL_GRACE_MS : entry.updatedAt + TOOL_EVENT_RECIPIENT_TTL_MS)) recipients.delete(runId); }; const add = (runId, connId) => { if (!runId || !connId) return; const now = Date.now(); const existing = recipients.get(runId); if (existing) { existing.connIds.add(connId); existing.updatedAt = now; } else recipients.set(runId, { connIds: new Set([connId]), updatedAt: now }); prune(); }; const get = (runId) => { const entry = recipients.get(runId); if (!entry) return; entry.updatedAt = Date.now(); prune(); return entry.connIds; }; const markFinal = (runId) => { const entry = recipients.get(runId); if (!entry) return; entry.finalizedAt = Date.now(); prune(); }; return { add, get, markFinal }; } //#endregion export { createToolEventRecipientRegistry as a, createSessionMessageSubscriberRegistry as i, createChatRunState as n, createSessionEventSubscriberRegistry as r, createChatRunRegistry as t };