openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
214 lines (213 loc) • 8.92 kB
JavaScript
import { a as buildProviderReplayFamilyHooks } from "../../provider-model-shared-fX-Ywqb3.js";
import { t as defineSingleProviderPluginEntry } from "../../provider-entry-CPCFGnL8.js";
import { r as buildProviderToolCompatFamilyHooks } from "../../provider-tools-D4EQoyQM.js";
import { a as normalizeClawRouterResolvedModel, i as normalizeClawRouterReasoningEfforts, n as buildClawRouterProviderConfig, o as normalizeClawRouterRootUrl, r as normalizeClawRouterApiBaseUrl, t as CLAWROUTER_REASONING_EFFORT_LEVELS } from "../../provider-catalog-CSDSmGhK.js";
import { t as wrapClawRouterProviderStream } from "../../stream-DcTjanHl.js";
import { n as normalizePerplexityToolSchemas, t as inspectPerplexityToolSchemas } from "../../tool-schemas-B5ukoK-Z.js";
import { t as fetchClawRouterUsage } from "../../usage-DCHv7HYx.js";
//#region extensions/clawrouter/openclaw.plugin.json
var openclaw_plugin_default = {
id: "clawrouter",
activation: { "onStartup": false },
enabledByDefault: true,
providers: ["clawrouter"],
contracts: { "usageProviders": ["clawrouter"] },
setup: { "providers": [{
"id": "clawrouter",
"envVars": ["CLAWROUTER_API_KEY"]
}] },
providerAuthChoices: [{
"provider": "clawrouter",
"method": "api-key",
"choiceId": "clawrouter-api-key",
"choiceLabel": "ClawRouter proxy key",
"choiceHint": "Approved models through one managed key",
"groupId": "clawrouter",
"groupLabel": "ClawRouter",
"groupHint": "Managed model access and quotas",
"optionKey": "clawrouterApiKey",
"cliFlag": "--clawrouter-api-key",
"cliOption": "--clawrouter-api-key <key>",
"cliDescription": "ClawRouter proxy key"
}],
configSchema: {
"type": "object",
"additionalProperties": false,
"properties": {}
}
};
//#endregion
//#region extensions/clawrouter/index.ts
const PROVIDER_ID = "clawrouter";
const ENV_VAR = "CLAWROUTER_API_KEY";
const openAiReplay = buildProviderReplayFamilyHooks({
family: "openai-compatible",
dropReasoningFromHistory: false
});
const anthropicReplay = buildProviderReplayFamilyHooks({ family: "native-anthropic-by-model" });
const googleReplay = buildProviderReplayFamilyHooks({ family: "google-gemini" });
const openAiTools = buildProviderToolCompatFamilyHooks("openai");
const deepSeekTools = buildProviderToolCompatFamilyHooks("deepseek");
const geminiTools = buildProviderToolCompatFamilyHooks("gemini");
const perplexityTools = {
normalizeToolSchemas: normalizePerplexityToolSchemas,
inspectToolSchemas: inspectPerplexityToolSchemas
};
function resolveClawRouterThinkingProfile(ctx) {
const efforts = normalizeClawRouterReasoningEfforts(ctx.compat?.supportedReasoningEfforts);
if (!efforts) return;
const supported = new Set(efforts);
const levels = CLAWROUTER_REASONING_EFFORT_LEVELS.filter(([effort]) => supported.has(effort)).map(([, id]) => ({ id }));
const runtime = ctx.agentRuntime?.trim().toLowerCase();
if (levels.some((level) => level.id === "max") && (runtime === "openclaw" || runtime === "auto")) levels.push({ id: "ultra" });
return { levels };
}
function configuredBaseUrl(config) {
const value = config?.models?.providers?.[PROVIDER_ID]?.baseUrl;
return typeof value === "string" ? value : void 0;
}
function dynamicModelScope(ctx) {
return JSON.stringify([
ctx.agentDir ?? "",
ctx.workspaceDir ?? "",
ctx.authProfileId ?? "",
normalizeClawRouterRootUrl(ctx.providerConfig?.baseUrl ?? configuredBaseUrl(ctx.config))
]);
}
function buildRuntimeModels(providerConfig) {
const models = /* @__PURE__ */ new Map();
for (const model of providerConfig.models) {
const api = model.api ?? providerConfig.api;
const baseUrl = model.baseUrl ?? providerConfig.baseUrl;
if (!api || !baseUrl) continue;
models.set(model.id, {
...model,
api,
baseUrl,
provider: PROVIDER_ID,
input: model.input.filter((entry) => entry === "text" || entry === "image")
});
}
return models;
}
function resolveToolFamily(modelId) {
const normalized = modelId.toLowerCase();
if (normalized.startsWith("deepseek/")) return deepSeekTools;
if (normalized.startsWith("google/")) return geminiTools;
if (normalized.startsWith("perplexity/")) return perplexityTools;
return openAiTools;
}
var clawrouter_default = defineSingleProviderPluginEntry({
id: PROVIDER_ID,
name: "ClawRouter",
description: "Managed multi-provider model routing and quotas",
manifest: openclaw_plugin_default,
provider() {
const dynamicModels = /* @__PURE__ */ new Map();
return {
label: "ClawRouter",
docsPath: "/providers/clawrouter",
manifestAuth: {
hint: "Credential-scoped access to approved models and budgets",
noteTitle: "ClawRouter",
noteMessage: ["Use the proxy key issued by your ClawRouter administrator.", "OpenClaw discovers only the models granted to that key."].join("\n")
},
catalog: {
order: "simple",
run: async (ctx) => {
const auth = ctx.resolveProviderAuth(PROVIDER_ID);
let discoveryApiKey = auth.discoveryApiKey;
if (!discoveryApiKey) try {
const { resolveApiKeyForProvider } = await import("../../plugin-sdk/provider-auth-runtime.js");
discoveryApiKey = (await resolveApiKeyForProvider({
provider: PROVIDER_ID,
cfg: ctx.config,
...ctx.agentDir ? { agentDir: ctx.agentDir } : {},
...ctx.workspaceDir ? { workspaceDir: ctx.workspaceDir } : {},
...auth.profileId ? {
profileId: auth.profileId,
lockedProfile: true
} : {}
}))?.apiKey;
} catch {
return null;
}
const apiKey = auth.apiKey ?? discoveryApiKey;
if (!apiKey || !discoveryApiKey) return null;
return { provider: await buildClawRouterProviderConfig({
apiKey,
discoveryApiKey,
baseUrl: configuredBaseUrl(ctx.config)
}) };
}
},
resolveDynamicModel: (ctx) => dynamicModels.get(dynamicModelScope(ctx))?.get(ctx.modelId),
preferRuntimeResolvedModel: (ctx) => {
const agentDir = ctx.agentDir ?? "";
const workspaceDir = ctx.workspaceDir ?? "";
const rootUrl = normalizeClawRouterRootUrl(configuredBaseUrl(ctx.config));
for (const [scope, models] of dynamicModels) {
const [scopeAgentDir, scopeWorkspaceDir, , scopeRootUrl] = JSON.parse(scope);
if (scopeAgentDir === agentDir && scopeWorkspaceDir === workspaceDir && scopeRootUrl === rootUrl && models.has(ctx.modelId)) return true;
}
return false;
},
prepareDynamicModel: async (ctx) => {
const scope = dynamicModelScope(ctx);
const { resolveApiKeyForProvider } = await import("../../plugin-sdk/provider-auth-runtime.js");
const apiKey = (await resolveApiKeyForProvider({
provider: PROVIDER_ID,
cfg: ctx.config,
...ctx.agentDir ? { agentDir: ctx.agentDir } : {},
...ctx.workspaceDir ? { workspaceDir: ctx.workspaceDir } : {},
...ctx.authProfileId ? {
profileId: ctx.authProfileId,
lockedProfile: true
} : {}
}))?.apiKey;
if (!apiKey) {
dynamicModels.delete(scope);
return;
}
const providerConfig = await buildClawRouterProviderConfig({
apiKey,
discoveryApiKey: apiKey,
baseUrl: ctx.providerConfig?.baseUrl ?? configuredBaseUrl(ctx.config)
});
dynamicModels.set(scope, buildRuntimeModels(providerConfig));
},
normalizeConfig: ({ providerConfig }) => {
const baseUrl = normalizeClawRouterApiBaseUrl(providerConfig.baseUrl);
return baseUrl !== providerConfig.baseUrl ? {
...providerConfig,
baseUrl
} : void 0;
},
normalizeResolvedModel: ({ model }) => normalizeClawRouterResolvedModel(model),
wrapSimpleCompletionStreamFn: wrapClawRouterProviderStream,
wrapStreamFn: wrapClawRouterProviderStream,
buildReplayPolicy: (ctx) => {
if (ctx.modelApi === "anthropic-messages") return anthropicReplay.buildReplayPolicy?.(ctx);
if (ctx.modelApi === "google-generative-ai") return googleReplay.buildReplayPolicy?.(ctx);
return openAiReplay.buildReplayPolicy?.(ctx);
},
sanitizeReplayHistory: (ctx) => ctx.modelApi === "google-generative-ai" ? googleReplay.sanitizeReplayHistory?.(ctx) : void 0,
resolveReasoningOutputMode: (ctx) => ctx.modelApi === "google-generative-ai" ? googleReplay.resolveReasoningOutputMode?.(ctx) : void 0,
resolveThinkingProfile: resolveClawRouterThinkingProfile,
normalizeToolSchemas: (ctx) => resolveToolFamily(ctx.modelId ?? "").normalizeToolSchemas(ctx),
inspectToolSchemas: (ctx) => resolveToolFamily(ctx.modelId ?? "").inspectToolSchemas(ctx),
isModernModelRef: () => true,
resolveUsageAuth: async (ctx) => {
const apiKey = ctx.resolveApiKeyFromConfigAndStore({ envDirect: [ctx.env[ENV_VAR]] });
return apiKey ? { token: apiKey } : null;
},
fetchUsageSnapshot: async (ctx) => await fetchClawRouterUsage({
token: ctx.token,
baseUrl: configuredBaseUrl(ctx.config),
timeoutMs: ctx.timeoutMs
})
};
}
});
//#endregion
export { clawrouter_default as default };