openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
130 lines (129 loc) • 5.19 kB
JavaScript
import { D as resolveExpiresAtMsFromDurationMs, g as isFutureDateTimestampMs } from "./number-coercion-CLj0HTDM.js";
import { t as findNormalizedProviderKey } from "./provider-id-DMd-TDFp.js";
import { t as pruneMapToMaxSize } from "./map-size-CNcWiFKu.js";
import { o as normalizeConfiguredProviderCatalogModelId } from "./model-ref-shared-Dz7QU0Lx.js";
import { r as resolveProviderRequestCapabilities } from "./provider-attribution-CDS-94hQ.js";
import "./provider-catalog-8ARoyQkL.js";
import { createHash } from "node:crypto";
//#region src/plugin-sdk/provider-catalog-shared.ts
const liveCatalogCache = /* @__PURE__ */ new Map();
function buildLiveCatalogCacheKey(parts) {
return createHash("sha256").update(JSON.stringify(parts)).digest("hex");
}
/**
* Caches one live catalog load promise by stable key parts for a short TTL.
*/
async function getCachedLiveCatalogValue(params) {
const rawNow = params.now?.() ?? Date.now();
const expiresAt = resolveExpiresAtMsFromDurationMs(params.ttlMs ?? 3e4, { nowMs: rawNow });
if (expiresAt === void 0) return await params.load();
const key = buildLiveCatalogCacheKey(params.keyParts);
const existing = liveCatalogCache.get(key);
if (existing) {
if (isFutureDateTimestampMs(existing.expiresAt, { nowMs: rawNow })) return await existing.value;
liveCatalogCache.delete(key);
}
const entry = {
expiresAt,
value: params.load()
};
pruneMapToMaxSize(liveCatalogCache, 99);
liveCatalogCache.set(key, entry);
let retain = false;
try {
const resolved = await entry.value;
retain = params.shouldCache?.(resolved) ?? true;
return resolved;
} finally {
if (!retain && liveCatalogCache.get(key) === entry) liveCatalogCache.delete(key);
}
}
/**
* Clears the process-local live catalog cache for tests and isolated plugin probes.
*/
function clearLiveCatalogCacheForTests() {
liveCatalogCache.clear();
}
function normalizeConfiguredCatalogModelInput(input) {
if (!Array.isArray(input)) return;
const normalized = input.filter((item) => item === "text" || item === "image" || item === "audio" || item === "video" || item === "document");
return normalized.length > 0 ? normalized : void 0;
}
function resolveConfiguredProviderModels(config, providerId) {
const providers = config?.models?.providers;
if (!providers || typeof providers !== "object") return [];
const providerKey = findNormalizedProviderKey(providers, providerId);
if (!providerKey) return [];
const providerConfig = providers[providerKey];
if (!providerConfig || typeof providerConfig !== "object") return [];
return Array.isArray(providerConfig.models) ? providerConfig.models : [];
}
/**
* Reads user-configured provider models as catalog entries for plugin discovery output.
*/
function readConfiguredProviderCatalogEntries(params) {
const provider = params.publishedProviderId ?? params.providerId;
const models = resolveConfiguredProviderModels(params.config, params.providerId);
const entries = [];
for (const model of models) {
if (!model || typeof model !== "object") continue;
const id = typeof model.id === "string" ? model.id.trim() : "";
if (!id) continue;
const normalizedId = normalizeConfiguredProviderCatalogModelId(provider, id);
const name = (typeof model.name === "string" ? model.name : normalizedId).trim() || normalizedId;
const contextWindow = typeof model.contextWindow === "number" && model.contextWindow > 0 ? model.contextWindow : void 0;
const reasoning = typeof model.reasoning === "boolean" ? model.reasoning : void 0;
const input = normalizeConfiguredCatalogModelInput(model.input);
entries.push({
provider,
id: normalizedId,
name,
...contextWindow ? { contextWindow } : {},
...reasoning !== void 0 ? { reasoning } : {},
...input ? { input } : {}
});
}
return entries;
}
function withStreamingUsageCompat(provider) {
if (!Array.isArray(provider.models) || provider.models.length === 0) return provider;
let changed = false;
const models = provider.models.map((model) => {
if (model.compat?.supportsUsageInStreaming !== void 0) return model;
changed = true;
return {
...model,
compat: {
...model.compat,
supportsUsageInStreaming: true
}
};
});
return changed ? {
...provider,
models
} : provider;
}
/**
* Returns whether a provider transport can report native usage while streaming.
*/
function supportsNativeStreamingUsageCompat(params) {
return resolveProviderRequestCapabilities({
provider: params.providerId,
api: "openai-completions",
baseUrl: params.baseUrl,
capability: "llm",
transport: "stream"
}).supportsNativeStreamingUsageCompat;
}
/**
* Marks models as streaming-usage compatible when provider transport capabilities allow it.
*/
function applyProviderNativeStreamingUsageCompat(params) {
return supportsNativeStreamingUsageCompat({
providerId: params.providerId,
baseUrl: params.providerConfig.baseUrl
}) ? withStreamingUsageCompat(params.providerConfig) : params.providerConfig;
}
//#endregion
export { supportsNativeStreamingUsageCompat as a, readConfiguredProviderCatalogEntries as i, clearLiveCatalogCacheForTests as n, getCachedLiveCatalogValue as r, applyProviderNativeStreamingUsageCompat as t };