openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
208 lines (207 loc) • 7.87 kB
JavaScript
import { F as resolveTimerTimeoutMs } from "./number-coercion-CLj0HTDM.js";
import { o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { y as ssrfPolicyFromHttpBaseUrlAllowedHostname } from "./ssrf-0QyXWOVG.js";
import { i as fetchWithSsrFGuard, s as withTrustedEnvProxyGuardedFetchMode } from "./fetch-guard-BMdGQhbb.js";
import { n as buildManifestModelProviderConfig } from "./provider-catalog-8ARoyQkL.js";
import "./fetch-runtime-Dww9PPyp.js";
import "./number-runtime-Cy4drVnh.js";
import "./string-coerce-runtime-GQa0ehRA.js";
import "./ssrf-runtime-Bum5C6NN.js";
import { n as buildLiveModelProviderConfig } from "./provider-catalog-live-runtime-EZZSw18v.js";
import "./provider-catalog-shared-B4jeC2Nd.js";
//#region extensions/huggingface/openclaw.plugin.json
var openclaw_plugin_default = {
id: "huggingface",
activation: { "onStartup": false },
enabledByDefault: true,
providers: ["huggingface"],
modelCatalog: {
"providers": { "huggingface": {
"baseUrl": "https://router.huggingface.co/v1",
"api": "openai-completions",
"models": [
{
"id": "deepseek-ai/DeepSeek-R1",
"name": "DeepSeek R1",
"reasoning": true,
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 8192,
"cost": {
"input": 3,
"output": 7,
"cacheRead": 3,
"cacheWrite": 3
}
},
{
"id": "deepseek-ai/DeepSeek-V3.1",
"name": "DeepSeek V3.1",
"reasoning": false,
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 8192,
"cost": {
"input": .6,
"output": 1.25,
"cacheRead": .6,
"cacheWrite": .6
}
},
{
"id": "openai/gpt-oss-120b",
"name": "GPT-OSS 120B",
"reasoning": false,
"input": ["text"],
"contextWindow": 131072,
"maxTokens": 8192,
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
}
}
]
} },
"discovery": { "huggingface": "refreshable" }
},
modelIdNormalization: { "providers": { "huggingface": { "stripPrefixes": ["huggingface/"] } } },
setup: { "providers": [{
"id": "huggingface",
"envVars": ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"]
}] },
providerAuthChoices: [{
"provider": "huggingface",
"method": "api-key",
"choiceId": "huggingface-api-key",
"appGuidedSecret": true,
"choiceLabel": "Hugging Face API key",
"choiceHint": "Inference API (HF token)",
"groupId": "huggingface",
"groupLabel": "Hugging Face",
"groupHint": "Inference API (HF token)",
"optionKey": "huggingfaceApiKey",
"cliFlag": "--huggingface-api-key",
"cliOption": "--huggingface-api-key <key>",
"cliDescription": "Hugging Face API key (HF token)"
}],
configSchema: {
"type": "object",
"additionalProperties": false,
"properties": { "discovery": {
"type": "object",
"additionalProperties": false,
"properties": { "enabled": { "type": "boolean" } }
} }
},
uiHints: {
"discovery": {
"label": "Model Discovery",
"help": "Plugin-owned controls for Hugging Face model auto-discovery."
},
"discovery.enabled": {
"label": "Enable Discovery",
"help": "When false, OpenClaw keeps the Hugging Face plugin available but skips implicit startup discovery from ambient Hugging Face credentials."
}
}
};
//#endregion
//#region extensions/huggingface/models.ts
const HUGGINGFACE_MANIFEST_CATALOG = openclaw_plugin_default.modelCatalog.providers.huggingface;
const HUGGINGFACE_BASE_URL = HUGGINGFACE_MANIFEST_CATALOG.baseUrl;
const HUGGINGFACE_POLICY_SUFFIXES = ["cheapest", "fastest"];
const HUGGINGFACE_DISCOVERY_TIMEOUT_MS = 3e4;
const HUGGINGFACE_DEFAULT_COST = {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0
};
const HUGGINGFACE_DEFAULT_CONTEXT_WINDOW = 131072;
const HUGGINGFACE_DEFAULT_MAX_TOKENS = 8192;
const HUGGINGFACE_MODEL_CATALOG = buildManifestModelProviderConfig({
providerId: "huggingface",
catalog: HUGGINGFACE_MANIFEST_CATALOG
}).models;
function isHuggingfacePolicyLocked(modelRef) {
const ref = modelRef.trim();
return HUGGINGFACE_POLICY_SUFFIXES.some((suffix) => ref.endsWith(`:${suffix}`) || ref === suffix);
}
function isReasoningModelHeuristic(modelId) {
const lower = normalizeLowercaseStringOrEmpty(modelId);
return lower.includes("r1") || lower.includes("reason") || lower.includes("thinking") || lower.includes("reasoner") || lower.includes("grok") || lower.includes("qwq");
}
function displayNameFromApiEntry(entry) {
const fromApi = typeof entry.name === "string" && entry.name.trim() || typeof entry.title === "string" && entry.title.trim() || typeof entry.display_name === "string" && entry.display_name.trim();
if (fromApi) return fromApi;
const base = entry.id.split("/").pop() ?? entry.id;
if (typeof entry.owned_by === "string" && entry.owned_by.trim()) return `${entry.owned_by.trim()}/${base}`;
return base.replace(/-/g, " ").replace(/\b(\w)/g, (c) => c.toUpperCase());
}
function readHuggingfaceModelRows(body) {
const data = body?.data;
if (!Array.isArray(data)) throw new Error("Hugging Face model discovery response must contain a data array");
return data;
}
function projectHuggingfaceModels(rows) {
const catalogById = new Map(HUGGINGFACE_MODEL_CATALOG.map((model) => [model.id, model]));
const seen = /* @__PURE__ */ new Set();
const models = [];
for (const row of rows) {
const entry = row;
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
if (!entry || !id || seen.has(id)) continue;
seen.add(id);
const modalities = entry?.architecture?.input_modalities;
const providers = Array.isArray(entry?.providers) ? entry.providers.filter((provider) => provider?.status !== "error") : [];
const providerContexts = providers.map((provider) => provider?.context_length).filter((context) => typeof context === "number" && context > 0);
const model = catalogById.get(id) ?? {
id,
name: displayNameFromApiEntry(entry),
reasoning: isReasoningModelHeuristic(id),
input: Array.isArray(modalities) && modalities.includes("image") ? ["text", "image"] : ["text"],
cost: HUGGINGFACE_DEFAULT_COST,
contextWindow: HUGGINGFACE_DEFAULT_CONTEXT_WINDOW,
maxTokens: HUGGINGFACE_DEFAULT_MAX_TOKENS
};
models.push({
...model,
contextWindow: providerContexts.length > 0 ? Math.min(...providerContexts) : model.contextWindow,
...providers.some((provider) => provider?.supports_tools === false) ? { compat: {
...model.compat,
supportsTools: false
} } : {}
});
}
return models;
}
async function discoverHuggingfaceModels(apiKey, timeoutMs = HUGGINGFACE_DISCOVERY_TIMEOUT_MS) {
const trimmedKey = apiKey?.trim();
if (!trimmedKey) return HUGGINGFACE_MODEL_CATALOG.map((model) => Object.assign({}, model));
const requestTimeoutMs = resolveTimerTimeoutMs(timeoutMs, HUGGINGFACE_DISCOVERY_TIMEOUT_MS);
return (await buildLiveModelProviderConfig({
providerId: "huggingface",
endpoint: `${HUGGINGFACE_BASE_URL}/models`,
providerConfig: {
baseUrl: HUGGINGFACE_BASE_URL,
api: "openai-completions"
},
models: HUGGINGFACE_MODEL_CATALOG.map((model) => Object.assign({}, model)),
discoveryApiKey: trimmedKey,
signal: AbortSignal.timeout(requestTimeoutMs),
timeoutMs: requestTimeoutMs,
ttlMs: 0,
readRows: readHuggingfaceModelRows,
buildRequestHeaders: () => ({
Authorization: `Bearer ${trimmedKey}`,
"Content-Type": "application/json"
}),
policy: ssrfPolicyFromHttpBaseUrlAllowedHostname(HUGGINGFACE_BASE_URL),
auditContext: "huggingface-model-discovery",
fetchGuard: (params) => fetchWithSsrFGuard(withTrustedEnvProxyGuardedFetchMode(params)),
projectRows: projectHuggingfaceModels
})).models;
}
//#endregion
export { isHuggingfacePolicyLocked as a, discoverHuggingfaceModels as i, HUGGINGFACE_MODEL_CATALOG as n, openclaw_plugin_default as o, HUGGINGFACE_POLICY_SUFFIXES as r, HUGGINGFACE_BASE_URL as t };