UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

243 lines (242 loc) • 10.4 kB
import { c as trackAsyncWork } from "./async-work-scope-BYeX3gh6.mjs"; import { t as createSubsystemLogger } from "./subsystem-I-uqdx4f.mjs"; import { a as resolveAgentDir } from "./agent-scope-config-CDYpzA0r.mjs"; import { d as getActivePluginRegistryVersion } from "./runtime-NsVZTERq.mjs"; import { f as getRuntimeAuthProfileStoreSnapshotRevision } from "./runtime-snapshots-kS-hnXTd.mjs"; import { t as externalCliDiscoveryForConfigStatus } from "./external-cli-discovery-CzdLbOjb.mjs"; import { a as resolveAuthProfileOrder } from "./order-BJ9-s7yV.mjs"; import { t as resolveEnvApiKey } from "./model-auth-env-RKKDA1-c.mjs"; import { x as resolveUsableCustomProviderApiKey } from "./model-auth-provider-config-C6nCoXKi.mjs"; import { n as ensureAuthProfileStore } from "./store-runtime-DZk7O5wt.mjs"; import { i as resolveLegacyInheritedAuthAgentId } from "./legacy-inherited-auth-dir-DmZI0WMx.mjs"; import { n as PROVIDER_USAGE_TIMEOUT_MS } from "./provider-usage.shared-B2Q7uCr7.mjs"; import { l as listProviderUsagePluginDescriptors } from "./provider-runtime-D-Xv0Y4z.mjs"; import "./auth-profiles-DnH-eEC0.mjs"; import "./model-auth-CU3YlZuV.mjs"; import { n as fingerprintAuthProfileOwnerShape, o as fingerprintResolvedProviderAuth, t as fingerprintAuthProfileCredential } from "./execution-auth-binding-CVDaRnoO.mjs"; import { t as loadProviderUsageSummary } from "./provider-usage.load-D_FiM0AM.mjs"; import { t as formatForLog } from "./ws-log-Lc1qgHtV.mjs"; //#region src/gateway/server-methods/provider-usage-runtime.ts let current; function sortedRecordEntries(value) { return Object.entries(value ?? {}).toSorted(([left], [right]) => left.localeCompare(right)); } function fingerprintProviderUsageCredentials(params) { const profiles = sortedRecordEntries(params.store.profiles).map(([profileId, credential]) => { return fingerprintAuthProfileCredential({ profileId, credential }) ?? fingerprintAuthProfileOwnerShape({ profileId, credential }) ?? `${profileId}:${credential.type}:${credential.provider}`; }); const direct = [...params.directApiKeys].toSorted(([left], [right]) => left.localeCompare(right)).map(([provider, resolved]) => [provider, fingerprintResolvedProviderAuth({ ...resolved, mode: "api-key" }) ?? null]); const selectedProfiles = params.providerIds.map((provider) => [provider, resolveAuthProfileOrder({ cfg: params.cfg, store: params.store, provider })]); return JSON.stringify({ profiles, direct, order: sortedRecordEntries(params.store.order), lastGood: sortedRecordEntries(params.store.lastGood), selectedProfiles }); } function resolveDirectApiKeys(config, providerIds) { const directApiKeys = /* @__PURE__ */ new Map(); for (const provider of providerIds) { const resolved = resolveUsableCustomProviderApiKey({ cfg: config, provider, env: process.env }) ?? resolveEnvApiKey(provider, process.env, { config }); if (!resolved) continue; directApiKeys.set(provider, resolved); } return directApiKeys; } function clearProviderUsageRuntimeSnapshot() { current = void 0; } function getProviderUsageRuntimeSnapshot(params) { const agentId = params.agentId ?? resolveLegacyInheritedAuthAgentId(params.config); const agentDir = params.agentDir ?? resolveAgentDir(params.config, agentId); const configRef = params.config; const pluginRegistryGeneration = getActivePluginRegistryVersion(); const authStoreGeneration = getRuntimeAuthProfileStoreSnapshotRevision(agentDir); if (current?.configRef === configRef && (params.store === void 0 || current.store === params.store) && current.agentDir === agentDir && current.agentId === agentId && current.pluginRegistryGeneration === pluginRegistryGeneration && current.authStoreGeneration === authStoreGeneration) return current; const store = params.store ?? ensureAuthProfileStore(agentDir, { externalCli: externalCliDiscoveryForConfigStatus({ cfg: configRef }) }); const providerIds = listProviderUsagePluginDescriptors({ config: configRef, env: process.env }).map((descriptor) => descriptor.provider); current = { agentDir, agentId, configRef, credentialKey: fingerprintProviderUsageCredentials({ cfg: configRef, directApiKeys: resolveDirectApiKeys(configRef, providerIds), providerIds, store }), providerIds, store, authStoreGeneration: getRuntimeAuthProfileStoreSnapshotRevision(agentDir), pluginRegistryGeneration }; return current; } //#endregion //#region src/gateway/server-methods/models-auth-status-usage-cache.ts const log = createSubsystemLogger("provider-usage-cache"); const USAGE_CACHE_TTL_MS = 6e4; const usageCacheByAgentId = /* @__PURE__ */ new Map(); const usageRefreshByAgentId = /* @__PURE__ */ new Map(); let cacheGeneration = 0; function clearModelAuthStatusUsageCache() { cacheGeneration += 1; usageCacheByAgentId.clear(); usageRefreshByAgentId.clear(); clearProviderUsageRuntimeSnapshot(); } function scopeProviderUsageCredentialKey(credentialKey, providerIds) { const parsed = JSON.parse(credentialKey); const providers = new Set(providerIds); return JSON.stringify({ ...parsed, direct: parsed.direct.filter(([provider, fingerprint]) => providers.has(provider) && fingerprint !== null) }); } function mapProviderUsage(usage) { const usageByProvider = /* @__PURE__ */ new Map(); for (const snap of usage.providers) usageByProvider.set(snap.provider, { windows: snap.windows, ...snap.summary ? { summary: snap.summary } : {}, ...snap.plan ? { plan: snap.plan } : {}, ...snap.billing?.length ? { billing: snap.billing } : {}, ...snap.accountEmail ? { accountEmail: snap.accountEmail } : {} }); return usageByProvider; } function retainLastGoodOnTimeout(summary, lastGood) { if (!lastGood) return summary; const lastGoodByProvider = new Map(lastGood.providers.filter((provider) => provider.error === void 0).map((provider) => [provider.provider, provider])); const retainedLastGood = summary.providers.some((provider) => provider.error === "Timeout" && lastGoodByProvider.has(provider.provider)); return { ...summary, updatedAt: retainedLastGood ? lastGood.updatedAt : summary.updatedAt, providers: summary.providers.map((provider) => provider.error === "Timeout" ? lastGoodByProvider.get(provider.provider) ?? provider : provider) }; } function scheduleProviderUsageRefresh(params) { const active = usageRefreshByAgentId.get(params.agentId); if (active?.agentDir === params.agentDir && active.configRef === params.configRef && active.credentialKey === params.credentialKey && active.providerKey === params.providerKey) return active.promise; const publishGeneration = cacheGeneration; const promise = trackAsyncWork(() => loadProviderUsageSummary({ providers: params.providerIds, agentDir: params.agentDir, authStore: params.authStore, config: params.configRef, timeoutMs: PROVIDER_USAGE_TIMEOUT_MS }).then((freshUsage) => { const usage = retainLastGoodOnTimeout(freshUsage, params.lastGood); if (publishGeneration === cacheGeneration && usageRefreshByAgentId.get(params.agentId) === refresh) usageCacheByAgentId.set(params.agentId, { agentDir: params.agentDir, configRef: params.configRef, credentialKey: params.credentialKey, providerKey: params.providerKey, refreshedAt: Date.now(), summary: usage, usageByProvider: mapProviderUsage(usage) }); return usage; }).catch((err) => { log.debug(`usage refresh failed: providers=${params.providerIds.join(",")} error=${formatForLog(err)}`); throw err; }).finally(() => { if (usageRefreshByAgentId.get(params.agentId) === refresh) usageRefreshByAgentId.delete(params.agentId); })); const refresh = { agentDir: params.agentDir, configRef: params.configRef, credentialKey: params.credentialKey, providerKey: params.providerKey, promise }; usageRefreshByAgentId.set(params.agentId, refresh); return promise; } function resolveProviderUsageCacheRead(params) { const providerIds = params.providerIds.toSorted(); const providerKey = providerIds.join("\0"); const credentialKey = scopeProviderUsageCredentialKey(params.credentialKey, providerIds); const cached = usageCacheByAgentId.get(params.agentId); const matching = cached?.agentDir === params.agentDir && cached.configRef === params.configRef && cached.credentialKey === credentialKey && cached.providerKey === providerKey ? cached : void 0; return { matching, needsRefresh: params.forceRefresh === true || !matching || params.now - matching.refreshedAt >= USAGE_CACHE_TTL_MS, refreshParams: { agentId: params.agentId, agentDir: params.agentDir, authStore: params.authStore, configRef: params.configRef, credentialKey, providerIds, providerKey, lastGood: matching?.summary } }; } function readProviderUsageStaleWhileRevalidate(params) { if (params.providerIds.length === 0) { usageCacheByAgentId.delete(params.agentId); return /* @__PURE__ */ new Map(); } const { matching, needsRefresh, refreshParams } = resolveProviderUsageCacheRead(params); if (needsRefresh) scheduleProviderUsageRefresh(refreshParams).catch(() => {}); return matching?.usageByProvider ?? /* @__PURE__ */ new Map(); } /** Shares the models.authStatus cache contract with the unscoped usage.status RPC. */ async function loadUsageStatusStaleWhileRevalidate(options) { const snapshot = getProviderUsageRuntimeSnapshot({ config: options.config }); const params = { agentId: snapshot.agentId, agentDir: snapshot.agentDir, authStore: snapshot.store, configRef: snapshot.configRef, credentialKey: snapshot.credentialKey, providerIds: snapshot.providerIds, coldRead: options.coldRead, now: options.now ?? Date.now() }; if (params.providerIds.length === 0) { usageCacheByAgentId.delete(params.agentId); return { updatedAt: params.now, providers: [] }; } const { matching, needsRefresh, refreshParams } = resolveProviderUsageCacheRead(params); if (matching && !needsRefresh) return matching.summary; const refresh = scheduleProviderUsageRefresh(refreshParams); if (matching) { refresh.catch(() => {}); return matching.summary; } if (params.coldRead !== "refresh-marker") return await refresh; refresh.catch(() => {}); return { updatedAt: params.now, providers: [], refreshing: true }; } //#endregion export { getProviderUsageRuntimeSnapshot as i, loadUsageStatusStaleWhileRevalidate as n, readProviderUsageStaleWhileRevalidate as r, clearModelAuthStatusUsageCache as t };