mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 07:01:34 +00:00
refactor(providers): derive simple providers from manifests (#114331)
This commit is contained in:
committed by
GitHub
parent
ccd6845e4d
commit
dc8fdb1756
@@ -3,12 +3,9 @@ import { buildOpenAICompatibleLiveModelProviderConfig } from "openclaw/plugin-sd
|
||||
import type { ProviderCatalogContext } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import {
|
||||
BASETEN_DEFAULT_MODEL_REF,
|
||||
projectBasetenLiveModels,
|
||||
resolveBasetenDynamicModel,
|
||||
} from "./models.js";
|
||||
import { projectBasetenLiveModels, resolveBasetenDynamicModel } from "./models.js";
|
||||
import { applyBasetenConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildStaticBasetenProvider } from "./provider-catalog.js";
|
||||
import { createBasetenThinkingWrapper } from "./stream.js";
|
||||
import { resolveBasetenThinkingProfile } from "./thinking.js";
|
||||
@@ -19,31 +16,18 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Baseten Provider",
|
||||
description: "Official Baseten Model APIs provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Baseten",
|
||||
docsPath: "/providers/baseten",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Baseten API key",
|
||||
hint: "Hosted Model APIs, including Inkling",
|
||||
optionKey: "basetenApiKey",
|
||||
flagName: "--baseten-api-key",
|
||||
envVar: "BASETEN_API_KEY",
|
||||
promptMessage: "Enter Baseten API key",
|
||||
defaultModel: BASETEN_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyBasetenConfig(cfg),
|
||||
noteTitle: "Baseten",
|
||||
noteMessage: [
|
||||
"Baseten hosts Thinking Machines Lab's Inkling and other frontier models behind one OpenAI-compatible API.",
|
||||
"Get your API key at: https://app.baseten.co/settings/api_keys",
|
||||
].join("\n"),
|
||||
wizard: {
|
||||
groupLabel: "Baseten",
|
||||
groupHint: "Hosted Model APIs, including Inkling",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
applyConfig: applyBasetenConfig,
|
||||
noteTitle: "Baseten",
|
||||
noteMessage: [
|
||||
"Baseten hosts Thinking Machines Lab's Inkling and other frontier models behind one OpenAI-compatible API.",
|
||||
"Get your API key at: https://app.baseten.co/settings/api_keys",
|
||||
].join("\n"),
|
||||
},
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: async (ctx: ProviderCatalogContext) => {
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* Cerebras provider plugin entrypoint.
|
||||
*/
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyCerebrasConfig, CEREBRAS_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildCerebrasProvider } from "./provider-catalog.js";
|
||||
import { applyCerebrasConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
const PROVIDER_ID = "cerebras";
|
||||
|
||||
@@ -11,36 +11,19 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Cerebras Provider",
|
||||
description: "Bundled Cerebras provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Cerebras",
|
||||
docsPath: "/providers/cerebras",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Cerebras API key",
|
||||
hint: "Fast OpenAI-compatible inference",
|
||||
optionKey: "cerebrasApiKey",
|
||||
flagName: "--cerebras-api-key",
|
||||
envVar: "CEREBRAS_API_KEY",
|
||||
promptMessage: "Enter Cerebras API key",
|
||||
defaultModel: CEREBRAS_DEFAULT_MODEL_REF,
|
||||
preserveExistingPrimary: true,
|
||||
applyConfig: (cfg) => applyCerebrasConfig(cfg),
|
||||
noteMessage: [
|
||||
"Cerebras provides high-speed OpenAI-compatible inference for GPT OSS and GLM models.",
|
||||
"Get your API key at: https://cloud.cerebras.ai",
|
||||
].join("\n"),
|
||||
noteTitle: "Cerebras",
|
||||
wizard: {
|
||||
groupLabel: "Cerebras",
|
||||
groupHint: "Fast OpenAI-compatible inference",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildCerebrasProvider,
|
||||
buildStaticProvider: buildCerebrasProvider,
|
||||
liveModelDiscovery: true,
|
||||
manifestAuth: {
|
||||
preserveExistingPrimary: true,
|
||||
applyConfig: applyCerebrasConfig,
|
||||
noteMessage: [
|
||||
"Cerebras provides high-speed OpenAI-compatible inference for GPT OSS and GLM models.",
|
||||
"Get your API key at: https://cloud.cerebras.ai",
|
||||
].join("\n"),
|
||||
noteTitle: "Cerebras",
|
||||
},
|
||||
catalog: { liveModelDiscovery: true },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
|
||||
import type { Context, Model } from "openclaw/plugin-sdk/llm";
|
||||
import { registerSingleProviderPlugin } from "openclaw/plugin-sdk/plugin-test-runtime";
|
||||
import { buildManifestModelProviderConfig } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import { buildOpenAICompletionsParams } from "openclaw/plugin-sdk/provider-transport-runtime";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import plugin from "./index.js";
|
||||
import { buildCohereProvider, COHERE_LIVE_MODEL_DISCOVERY } from "./provider-catalog.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { COHERE_LIVE_MODEL_DISCOVERY } from "./provider-catalog.js";
|
||||
import { createCohereCompletionsWrapper } from "./stream.js";
|
||||
|
||||
const COHERE_COMMAND_A_PLUS_MODEL_ID = "command-a-plus-05-2026";
|
||||
@@ -13,11 +14,11 @@ const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
|
||||
const COHERE_COMMAND_A_VISION_MODEL_ID = "command-a-vision-07-2025";
|
||||
const COHERE_NORTH_MINI_CODE_MODEL_ID = "north-mini-code-1-0";
|
||||
|
||||
function readManifest() {
|
||||
return JSON.parse(readFileSync(new URL("./openclaw.plugin.json", import.meta.url), "utf8")) as {
|
||||
providerAuthChoices?: Array<{ choiceId?: string; optionKey?: string; cliFlag?: string }>;
|
||||
setup?: { providers?: Array<{ id?: string; envVars?: string[] }> };
|
||||
};
|
||||
function buildCohereProvider() {
|
||||
return buildManifestModelProviderConfig({
|
||||
providerId: "cohere",
|
||||
catalog: manifest.modelCatalog.providers.cohere,
|
||||
});
|
||||
}
|
||||
|
||||
function requireCohereModel(modelId = COHERE_COMMAND_A_PLUS_MODEL_ID): Model<"openai-completions"> {
|
||||
@@ -78,16 +79,14 @@ describe("Cohere provider plugin", () => {
|
||||
kind: "api_key",
|
||||
wizard: { choiceId: "cohere-api-key" },
|
||||
});
|
||||
expect(readManifest().providerAuthChoices).toEqual([
|
||||
expect(manifest.providerAuthChoices).toEqual([
|
||||
expect.objectContaining({
|
||||
choiceId: "cohere-api-key",
|
||||
optionKey: "cohereApiKey",
|
||||
cliFlag: "--cohere-api-key",
|
||||
}),
|
||||
]);
|
||||
expect(readManifest().setup?.providers).toEqual([
|
||||
{ id: "cohere", envVars: ["COHERE_API_KEY"] },
|
||||
]);
|
||||
expect(manifest.setup.providers).toEqual([{ id: "cohere", envVars: ["COHERE_API_KEY"] }]);
|
||||
});
|
||||
|
||||
it("exposes the static Cohere catalog", () => {
|
||||
|
||||
@@ -1,36 +1,20 @@
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { isModernCohereModelId } from "./models.js";
|
||||
import { applyCohereConfig, COHERE_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildCohereProvider, COHERE_LIVE_MODEL_DISCOVERY } from "./provider-catalog.js";
|
||||
import { applyCohereConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { COHERE_LIVE_MODEL_DISCOVERY } from "./provider-catalog.js";
|
||||
import { createCohereCompletionsWrapper } from "./stream.js";
|
||||
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: "cohere",
|
||||
name: "Cohere Provider",
|
||||
description: "Cohere provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Cohere",
|
||||
docsPath: "/providers/cohere",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Cohere API key",
|
||||
hint: "OpenAI-compatible inference",
|
||||
optionKey: "cohereApiKey",
|
||||
flagName: "--cohere-api-key",
|
||||
envVar: "COHERE_API_KEY",
|
||||
promptMessage: "Enter Cohere API key",
|
||||
defaultModel: COHERE_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyCohereConfig(cfg),
|
||||
wizard: {
|
||||
groupLabel: "Cohere",
|
||||
groupHint: "OpenAI-compatible inference",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: { applyConfig: applyCohereConfig },
|
||||
catalog: {
|
||||
buildProvider: buildCohereProvider,
|
||||
buildStaticProvider: buildCohereProvider,
|
||||
liveModelDiscovery: COHERE_LIVE_MODEL_DISCOVERY,
|
||||
},
|
||||
wrapStreamFn: (ctx) => createCohereCompletionsWrapper(ctx.streamFn),
|
||||
|
||||
@@ -2,9 +2,10 @@ import type { OpenClawConfig } from "openclaw/plugin-sdk/config-contracts";
|
||||
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildCohereCatalogModels, COHERE_BASE_URL } from "./models.js";
|
||||
import { applyCohereConfig, COHERE_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { applyCohereConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
const COHERE_DEFAULT_MODEL_REF = `cohere/${manifest.modelCatalog.providers.cohere.defaultModel}`;
|
||||
const COHERE_DEFAULT_MODEL_ID = "command-a-plus-05-2026";
|
||||
const COHERE_COMMAND_A_REASONING_MODEL_ID = "command-a-reasoning-08-2025";
|
||||
const COHERE_COMMAND_A_VISION_MODEL_ID = "command-a-vision-07-2025";
|
||||
|
||||
@@ -6,7 +6,7 @@ import {
|
||||
import { buildCohereCatalogModels, COHERE_BASE_URL } from "./models.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
export const COHERE_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "cohere")!;
|
||||
const COHERE_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "cohere")!;
|
||||
|
||||
const coherePresetAppliers = createModelCatalogPresetAppliers({
|
||||
primaryModelRef: COHERE_DEFAULT_MODEL_REF,
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import type { OpenAICompatibleModelDiscoveryOptions } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
|
||||
import type { ModelProviderConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildCohereCatalogModels, COHERE_BASE_URL } from "./models.js";
|
||||
import { COHERE_BASE_URL } from "./models.js";
|
||||
|
||||
export const COHERE_LIVE_MODEL_DISCOVERY: OpenAICompatibleModelDiscoveryOptions = {
|
||||
endpointUrl: {
|
||||
@@ -25,11 +24,3 @@ export const COHERE_LIVE_MODEL_DISCOVERY: OpenAICompatibleModelDiscoveryOptions
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export function buildCohereProvider(): ModelProviderConfig {
|
||||
return {
|
||||
baseUrl: COHERE_BASE_URL,
|
||||
api: "openai-completions",
|
||||
models: buildCohereCatalogModels(),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-en
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { fetchDeepSeekUsage } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { applyDeepSeekConfig, DEEPSEEK_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { applyDeepSeekConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildDeepSeekProvider } from "./provider-catalog.js";
|
||||
import { createDeepSeekV4ThinkingWrapper } from "./stream.js";
|
||||
import { resolveDeepSeekV4ThinkingProfile } from "./thinking.js";
|
||||
@@ -15,29 +16,11 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "DeepSeek Provider",
|
||||
description: "Bundled DeepSeek provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "DeepSeek",
|
||||
docsPath: "/providers/deepseek",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "DeepSeek API key",
|
||||
hint: "API key",
|
||||
optionKey: "deepseekApiKey",
|
||||
flagName: "--deepseek-api-key",
|
||||
envVar: "DEEPSEEK_API_KEY",
|
||||
promptMessage: "Enter DeepSeek API key",
|
||||
defaultModel: DEEPSEEK_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyDeepSeekConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "deepseek-api-key",
|
||||
choiceLabel: "DeepSeek API key",
|
||||
groupId: "deepseek",
|
||||
groupLabel: "DeepSeek",
|
||||
groupHint: "API key",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: { applyConfig: applyDeepSeekConfig },
|
||||
catalog: {
|
||||
buildProvider: buildDeepSeekProvider,
|
||||
buildStaticProvider: buildDeepSeekProvider,
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { resolveAgentModelPrimaryValue } from "openclaw/plugin-sdk/provider-onboard";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { applyDeepSeekConfig, DEEPSEEK_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { applyDeepSeekConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
const DEEPSEEK_DEFAULT_MODEL_REF = `deepseek/${manifest.modelCatalog.providers.deepseek.defaultModel}`;
|
||||
|
||||
describe("DeepSeek onboarding", () => {
|
||||
it("applies the manifest catalog, default, and alias", () => {
|
||||
const config = applyDeepSeekConfig({});
|
||||
|
||||
@@ -6,10 +6,7 @@ import {
|
||||
import { DEEPSEEK_BASE_URL, DEEPSEEK_MODEL_CATALOG } from "./models.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
export const DEEPSEEK_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(
|
||||
manifest,
|
||||
"deepseek",
|
||||
)!;
|
||||
const DEEPSEEK_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "deepseek")!;
|
||||
|
||||
const deepSeekPresetAppliers = createModelCatalogPresetAppliers({
|
||||
primaryModelRef: DEEPSEEK_DEFAULT_MODEL_REF,
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
} from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { applyFeatherlessConfig, FEATHERLESS_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import {
|
||||
buildFeatherlessProvider,
|
||||
FEATHERLESS_BASE_URL,
|
||||
FEATHERLESS_DEFAULT_MODEL_ID,
|
||||
FEATHERLESS_DYNAMIC_COMPAT,
|
||||
@@ -75,31 +75,20 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Featherless AI Provider",
|
||||
description: "Featherless AI provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Featherless AI",
|
||||
docsPath: "/providers/featherless",
|
||||
envVars: ["FEATHERLESS_API_KEY"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Featherless AI API key",
|
||||
hint: "OpenAI-compatible access to open models",
|
||||
optionKey: "featherlessApiKey",
|
||||
flagName: "--featherless-api-key",
|
||||
envVar: "FEATHERLESS_API_KEY",
|
||||
promptMessage: "Enter Featherless AI API key",
|
||||
defaultModel: FEATHERLESS_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyFeatherlessConfig(cfg),
|
||||
noteTitle: "Featherless AI",
|
||||
noteMessage: [
|
||||
"Featherless AI serves open models through an OpenAI-compatible API.",
|
||||
"Create an API key at: https://featherless.ai/account/api-keys",
|
||||
].join("\n"),
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: FEATHERLESS_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyFeatherlessConfig,
|
||||
noteTitle: "Featherless AI",
|
||||
noteMessage: [
|
||||
"Featherless AI serves open models through an OpenAI-compatible API.",
|
||||
"Create an API key at: https://featherless.ai/account/api-keys",
|
||||
].join("\n"),
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildFeatherlessProvider,
|
||||
buildStaticProvider: buildFeatherlessProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
liveModelDiscovery: {
|
||||
endpointPath: "models?capabilities=chat",
|
||||
|
||||
@@ -8,13 +8,12 @@ import {
|
||||
} from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { isFireworksKimiModelId } from "./model-id.js";
|
||||
import { applyFireworksConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import {
|
||||
buildFireworksProvider,
|
||||
FIREWORKS_BASE_URL,
|
||||
FIREWORKS_DEFAULT_CONTEXT_WINDOW,
|
||||
FIREWORKS_DEFAULT_MAX_TOKENS,
|
||||
FIREWORKS_DEFAULT_MODEL_ID,
|
||||
FIREWORKS_DEFAULT_MODEL_REF,
|
||||
isFireworksCatalogModelId,
|
||||
} from "./provider-catalog.js";
|
||||
import { wrapFireworksProviderStream } from "./stream.js";
|
||||
@@ -76,26 +75,13 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Fireworks Provider",
|
||||
description: "Bundled Fireworks AI provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Fireworks",
|
||||
aliases: ["fireworks-ai"],
|
||||
docsPath: "/providers/fireworks",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Fireworks API key",
|
||||
hint: "API key",
|
||||
optionKey: "fireworksApiKey",
|
||||
flagName: "--fireworks-api-key",
|
||||
envVar: "FIREWORKS_API_KEY",
|
||||
promptMessage: "Enter Fireworks API key",
|
||||
defaultModel: FIREWORKS_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyFireworksConfig(cfg),
|
||||
},
|
||||
],
|
||||
manifestAuth: { applyConfig: applyFireworksConfig },
|
||||
catalog: {
|
||||
buildProvider: buildFireworksProvider,
|
||||
buildStaticProvider: buildFireworksProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
liveModelDiscovery: true,
|
||||
},
|
||||
|
||||
@@ -3,7 +3,7 @@ import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provid
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { GMI_DEFAULT_MODEL_REF } from "./models.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildGmiProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "gmi";
|
||||
@@ -12,25 +12,15 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "GMI Cloud Provider",
|
||||
description: "GMI Cloud provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "GMI Cloud",
|
||||
docsPath: "/providers/gmi",
|
||||
aliases: ["gmi-cloud", "gmicloud"],
|
||||
envVars: ["GMI_API_KEY"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "GMI Cloud API key",
|
||||
hint: "OpenAI-compatible GMI Cloud endpoint",
|
||||
optionKey: "gmiApiKey",
|
||||
flagName: "--gmi-api-key",
|
||||
envVar: "GMI_API_KEY",
|
||||
promptMessage: "Enter GMI Cloud API key",
|
||||
defaultModel: GMI_DEFAULT_MODEL_REF,
|
||||
noteTitle: "GMI Cloud",
|
||||
noteMessage: "Manage API keys at https://www.gmicloud.ai/",
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
noteTitle: "GMI Cloud",
|
||||
noteMessage: "Manage API keys at https://www.gmicloud.ai/",
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildGmiProvider,
|
||||
buildStaticProvider: buildGmiProvider,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// Gmi plugin module implements models behavior.
|
||||
import {
|
||||
buildManifestModelDefinition,
|
||||
readManifestProviderDefaultModelRef,
|
||||
} from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
@@ -15,4 +12,3 @@ export const GMI_MODEL_CATALOG: ModelDefinitionConfig[] = GMI_MANIFEST_CATALOG.m
|
||||
decorate: (model) => ({ ...model, api: "openai-completions" }),
|
||||
}),
|
||||
);
|
||||
export const GMI_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "gmi")!;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Huggingface plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyHuggingfaceConfig, HUGGINGFACE_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildHuggingfaceProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "huggingface";
|
||||
@@ -15,23 +16,15 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Hugging Face Provider",
|
||||
description: "Bundled Hugging Face provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Hugging Face",
|
||||
docsPath: "/providers/huggingface",
|
||||
envVars: ["HUGGINGFACE_HUB_TOKEN", "HF_TOKEN"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Hugging Face API key",
|
||||
hint: "Inference API (HF token)",
|
||||
optionKey: "huggingfaceApiKey",
|
||||
flagName: "--huggingface-api-key",
|
||||
envVar: "HUGGINGFACE_HUB_TOKEN",
|
||||
promptMessage: "Enter Hugging Face API key",
|
||||
defaultModel: HUGGINGFACE_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyHuggingfaceConfig(cfg),
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: HUGGINGFACE_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyHuggingfaceConfig,
|
||||
},
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: async (ctx) => {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provid
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { applyKilocodeConfig, KILOCODE_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildKilocodeProvider, buildKilocodeProviderWithDiscovery } from "./provider-catalog.js";
|
||||
import { wrapKilocodeProviderStream } from "./stream.js";
|
||||
|
||||
@@ -12,22 +13,14 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Kilo Gateway Provider",
|
||||
description: "Bundled Kilo Gateway provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Kilo Gateway",
|
||||
docsPath: "/providers/kilocode",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Kilo Gateway API key",
|
||||
hint: "API key (OpenRouter-compatible)",
|
||||
optionKey: "kilocodeApiKey",
|
||||
flagName: "--kilocode-api-key",
|
||||
envVar: "KILOCODE_API_KEY",
|
||||
promptMessage: "Enter Kilo Gateway API key",
|
||||
defaultModel: KILOCODE_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyKilocodeConfig(cfg),
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: KILOCODE_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyKilocodeConfig,
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildKilocodeProviderWithDiscovery,
|
||||
buildStaticProvider: buildKilocodeProvider,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-mod
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { LONGCAT_DEFAULT_MODEL_REF } from "./models.js";
|
||||
import { applyLongCatConfig } from "./onboard.js";
|
||||
import { buildLongCatProvider } from "./provider-catalog.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { createLongCatThinkingWrapper } from "./stream.js";
|
||||
|
||||
const PROVIDER_ID = "longcat";
|
||||
@@ -13,38 +13,18 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "LongCat Provider",
|
||||
description: "Official LongCat provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "LongCat",
|
||||
docsPath: "/providers/longcat",
|
||||
aliases: ["meituan-longcat"],
|
||||
envVars: ["LONGCAT_API_KEY"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "LongCat API key",
|
||||
hint: "API key",
|
||||
optionKey: "longcatApiKey",
|
||||
flagName: "--longcat-api-key",
|
||||
envVar: "LONGCAT_API_KEY",
|
||||
promptMessage: "Enter LongCat API key",
|
||||
defaultModel: LONGCAT_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyLongCatConfig(cfg),
|
||||
noteTitle: "LongCat",
|
||||
noteMessage: "Manage API keys at https://longcat.chat/platform/api_keys",
|
||||
wizard: {
|
||||
choiceId: "longcat-api-key",
|
||||
choiceLabel: "LongCat API key",
|
||||
groupId: "longcat",
|
||||
groupLabel: "LongCat",
|
||||
groupHint: "API key",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildLongCatProvider,
|
||||
buildStaticProvider: buildLongCatProvider,
|
||||
liveModelDiscovery: true,
|
||||
manifestAuth: {
|
||||
defaultModel: LONGCAT_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyLongCatConfig,
|
||||
noteTitle: "LongCat",
|
||||
noteMessage: "Manage API keys at https://longcat.chat/platform/api_keys",
|
||||
},
|
||||
catalog: { liveModelDiscovery: true },
|
||||
...buildProviderReplayFamilyHooks({
|
||||
family: "openai-compatible",
|
||||
dropReasoningFromHistory: false,
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
*/
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { applyMetaConfig, META_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { applyMetaConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildMetaProvider } from "./provider-catalog.js";
|
||||
import { wrapMetaProviderStream } from "./stream.js";
|
||||
import { resolveMetaThinkingProfile } from "./thinking.js";
|
||||
@@ -14,28 +15,15 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Meta Provider",
|
||||
description: "Bundled Meta provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Meta",
|
||||
docsPath: "/providers/meta",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Meta API key",
|
||||
hint: "Meta (Responses API)",
|
||||
optionKey: "metaApiKey",
|
||||
flagName: "--meta-api-key",
|
||||
envVar: "MODEL_API_KEY",
|
||||
promptMessage: "Enter Meta API key",
|
||||
defaultModel: META_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyMetaConfig(cfg),
|
||||
noteMessage: ["Meta provides Responses API inference."].join("\n"),
|
||||
noteTitle: "Meta",
|
||||
wizard: {
|
||||
groupLabel: "Meta",
|
||||
groupHint: "Meta (Responses API)",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
applyConfig: applyMetaConfig,
|
||||
noteMessage: "Meta provides Responses API inference.",
|
||||
noteTitle: "Meta",
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildMetaProvider,
|
||||
buildStaticProvider: buildMetaProvider,
|
||||
|
||||
@@ -7,8 +7,8 @@ import {
|
||||
} from "./api.js";
|
||||
import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js";
|
||||
import { mistralMemoryEmbeddingProviderAdapter } from "./memory-embedding-adapter.js";
|
||||
import { MISTRAL_DEFAULT_MODEL_REF } from "./model-definitions.js";
|
||||
import { applyMistralConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildMistralProvider } from "./provider-catalog.js";
|
||||
import { buildMistralRealtimeTranscriptionProvider } from "./realtime-transcription-provider.js";
|
||||
|
||||
@@ -24,25 +24,11 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Mistral Provider",
|
||||
description: "Bundled Mistral provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Mistral",
|
||||
docsPath: "/providers/models",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Mistral API key",
|
||||
hint: "API key",
|
||||
optionKey: "mistralApiKey",
|
||||
flagName: "--mistral-api-key",
|
||||
envVar: "MISTRAL_API_KEY",
|
||||
promptMessage: "Enter Mistral API key",
|
||||
defaultModel: MISTRAL_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyMistralConfig(cfg),
|
||||
wizard: {
|
||||
groupLabel: "Mistral AI",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: { applyConfig: applyMistralConfig },
|
||||
catalog: {
|
||||
buildProvider: buildMistralProvider,
|
||||
buildStaticProvider: buildMistralProvider,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { readConfiguredProviderCatalogEntries } from "openclaw/plugin-sdk/provid
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildProviderReplayFamilyHooks } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { buildProviderToolCompatFamilyHooks } from "openclaw/plugin-sdk/provider-tools";
|
||||
import { NOVITA_DEFAULT_MODEL_REF } from "./models.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildNovitaProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "novita";
|
||||
@@ -12,25 +12,15 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "NovitaAI Provider",
|
||||
description: "Bundled NovitaAI provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "NovitaAI",
|
||||
docsPath: "/providers/novita",
|
||||
aliases: ["novita-ai", "novitaai"],
|
||||
envVars: ["NOVITA_API_KEY"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "NovitaAI API key",
|
||||
hint: "OpenAI-compatible NovitaAI endpoint",
|
||||
optionKey: "novitaApiKey",
|
||||
flagName: "--novita-api-key",
|
||||
envVar: "NOVITA_API_KEY",
|
||||
promptMessage: "Enter NovitaAI API key",
|
||||
defaultModel: NOVITA_DEFAULT_MODEL_REF,
|
||||
noteTitle: "NovitaAI",
|
||||
noteMessage: "Manage API keys at https://novita.ai/settings/key-management",
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
noteTitle: "NovitaAI",
|
||||
noteMessage: "Manage API keys at https://novita.ai/settings/key-management",
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildNovitaProvider,
|
||||
buildStaticProvider: buildNovitaProvider,
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
// Novita plugin module implements models behavior.
|
||||
import {
|
||||
buildManifestModelDefinition,
|
||||
readManifestProviderDefaultModelRef,
|
||||
} from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import { buildManifestModelDefinition } from "openclaw/plugin-sdk/provider-catalog-shared";
|
||||
import type { ModelDefinitionConfig } from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
@@ -16,4 +13,3 @@ export const NOVITA_MODEL_CATALOG: ModelDefinitionConfig[] = NOVITA_MANIFEST_CAT
|
||||
decorate: (model) => ({ ...model, api: "openai-completions" }),
|
||||
}),
|
||||
);
|
||||
export const NOVITA_DEFAULT_MODEL_REF = readManifestProviderDefaultModelRef(manifest, "novita")!;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Nvidia plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyNvidiaConfig, NVIDIA_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import {
|
||||
buildLiveNvidiaProvider,
|
||||
buildSelectableNvidiaProvider,
|
||||
@@ -39,24 +40,15 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "NVIDIA Provider",
|
||||
description: "Bundled NVIDIA provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "NVIDIA",
|
||||
docsPath: "/providers/nvidia",
|
||||
envVars: ["NVIDIA_API_KEY"],
|
||||
preserveLiteralProviderPrefix: true,
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "NVIDIA API key",
|
||||
hint: "Direct API key",
|
||||
optionKey: "nvidiaApiKey",
|
||||
flagName: "--nvidia-api-key",
|
||||
envVar: "NVIDIA_API_KEY",
|
||||
promptMessage: "Enter NVIDIA API key",
|
||||
defaultModel: NVIDIA_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyNvidiaConfig,
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: NVIDIA_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyNvidiaConfig,
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildSelectableLiveNvidiaProvider,
|
||||
buildStaticProvider: buildSelectableNvidiaProvider,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Qianfan plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyQianfanConfig, QIANFAN_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildQianfanProvider } from "./provider-catalog.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
|
||||
const PROVIDER_ID = "qianfan";
|
||||
|
||||
@@ -9,26 +9,14 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Qianfan Provider",
|
||||
description: "Bundled Qianfan provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Qianfan",
|
||||
docsPath: "/providers/qianfan",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Qianfan API key",
|
||||
hint: "API key",
|
||||
optionKey: "qianfanApiKey",
|
||||
flagName: "--qianfan-api-key",
|
||||
envVar: "QIANFAN_API_KEY",
|
||||
promptMessage: "Enter Qianfan API key",
|
||||
defaultModel: QIANFAN_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyQianfanConfig(cfg),
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildQianfanProvider,
|
||||
buildStaticProvider: buildQianfanProvider,
|
||||
liveModelDiscovery: true,
|
||||
manifestAuth: {
|
||||
defaultModel: QIANFAN_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyQianfanConfig,
|
||||
},
|
||||
catalog: { liveModelDiscovery: true },
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Synthetic plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applySyntheticConfig, SYNTHETIC_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildSyntheticProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "synthetic";
|
||||
@@ -9,22 +10,14 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Synthetic Provider",
|
||||
description: "Bundled Synthetic provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Synthetic",
|
||||
docsPath: "/providers/synthetic",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Synthetic API key",
|
||||
hint: "Anthropic-compatible (multi-model)",
|
||||
optionKey: "syntheticApiKey",
|
||||
flagName: "--synthetic-api-key",
|
||||
envVar: "SYNTHETIC_API_KEY",
|
||||
promptMessage: "Enter Synthetic API key",
|
||||
defaultModel: SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applySyntheticConfig(cfg),
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: SYNTHETIC_DEFAULT_MODEL_REF,
|
||||
applyConfig: applySyntheticConfig,
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildSyntheticProvider,
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Together plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyTogetherConfig, TOGETHER_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildTogetherProvider } from "./provider-catalog.js";
|
||||
import { applyTogetherConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildTogetherVideoGenerationProvider } from "./video-generation-provider.js";
|
||||
|
||||
const PROVIDER_ID = "together";
|
||||
@@ -10,30 +10,12 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Together Provider",
|
||||
description: "Bundled Together provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Together",
|
||||
docsPath: "/providers/together",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Together AI API key",
|
||||
hint: "API key",
|
||||
optionKey: "togetherApiKey",
|
||||
flagName: "--together-api-key",
|
||||
envVar: "TOGETHER_API_KEY",
|
||||
promptMessage: "Enter Together AI API key",
|
||||
defaultModel: TOGETHER_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyTogetherConfig(cfg),
|
||||
wizard: {
|
||||
groupLabel: "Together AI",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildTogetherProvider,
|
||||
buildStaticProvider: buildTogetherProvider,
|
||||
liveModelDiscovery: true,
|
||||
},
|
||||
manifestAuth: { applyConfig: applyTogetherConfig },
|
||||
catalog: { liveModelDiscovery: true },
|
||||
classifyFailoverReason: ({ errorMessage }) =>
|
||||
/\bconcurrency limit\b.*\b(?:breached|reached)\b/i.test(errorMessage)
|
||||
? "rate_limit"
|
||||
|
||||
@@ -5,8 +5,9 @@ import {
|
||||
type ModelCompatConfig,
|
||||
} from "openclaw/plugin-sdk/provider-model-shared";
|
||||
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/string-coerce-runtime";
|
||||
import { VENICE_DEFAULT_MODEL_REF, VENICE_MODEL_DISCOVERY_OPTIONS } from "./models.js";
|
||||
import { VENICE_MODEL_DISCOVERY_OPTIONS } from "./models.js";
|
||||
import { applyVeniceConfig } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import { buildStaticVeniceProvider } from "./provider-catalog.js";
|
||||
import { createVeniceDeepSeekV4Wrapper } from "./stream.js";
|
||||
import { fetchVeniceUsage } from "./usage.js";
|
||||
@@ -37,31 +38,19 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Venice Provider",
|
||||
description: "Bundled Venice provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Venice",
|
||||
docsPath: "/providers/venice",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Venice AI API key",
|
||||
hint: "Privacy-focused (uncensored models)",
|
||||
optionKey: "veniceApiKey",
|
||||
flagName: "--venice-api-key",
|
||||
envVar: "VENICE_API_KEY",
|
||||
promptMessage: "Enter Venice AI API key",
|
||||
defaultModel: VENICE_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyVeniceConfig(cfg),
|
||||
noteMessage: [
|
||||
"Venice AI provides privacy-focused inference with uncensored models.",
|
||||
"Get your API key at: https://venice.ai/settings/api",
|
||||
"Supports 'private' (fully private) and 'anonymized' (proxy) modes.",
|
||||
].join("\n"),
|
||||
noteTitle: "Venice AI",
|
||||
wizard: {
|
||||
groupLabel: "Venice AI",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
applyConfig: applyVeniceConfig,
|
||||
noteMessage: [
|
||||
"Venice AI provides privacy-focused inference with uncensored models.",
|
||||
"Get your API key at: https://venice.ai/settings/api",
|
||||
"Supports 'private' (fully private) and 'anonymized' (proxy) modes.",
|
||||
].join("\n"),
|
||||
noteTitle: "Venice AI",
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildStaticVeniceProvider,
|
||||
liveModelDiscovery: VENICE_MODEL_DISCOVERY_OPTIONS,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Vercel Ai Gateway plugin entrypoint registers its OpenClaw integration.
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyVercelAiGatewayConfig, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import manifest from "./openclaw.plugin.json" with { type: "json" };
|
||||
import {
|
||||
buildStaticVercelAiGatewayProvider,
|
||||
buildVercelAiGatewayProvider,
|
||||
@@ -14,26 +15,14 @@ export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Vercel AI Gateway Provider",
|
||||
description: "Bundled Vercel AI Gateway provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Vercel AI Gateway",
|
||||
docsPath: "/providers/vercel-ai-gateway",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Vercel AI Gateway API key",
|
||||
hint: "API key",
|
||||
optionKey: "aiGatewayApiKey",
|
||||
flagName: "--ai-gateway-api-key",
|
||||
envVar: "AI_GATEWAY_API_KEY",
|
||||
promptMessage: "Enter Vercel AI Gateway API key",
|
||||
defaultModel: VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyVercelAiGatewayConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "ai-gateway-api-key",
|
||||
groupId: "ai-gateway",
|
||||
},
|
||||
},
|
||||
],
|
||||
manifestAuth: {
|
||||
defaultModel: VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF,
|
||||
applyConfig: applyVercelAiGatewayConfig,
|
||||
},
|
||||
catalog: {
|
||||
buildProvider: buildVercelAiGatewayProvider,
|
||||
buildStaticProvider: buildStaticVercelAiGatewayProvider,
|
||||
|
||||
@@ -21,6 +21,37 @@ function createModel(id: string, name: string): ModelDefinitionConfig {
|
||||
maxTokens: 8_192,
|
||||
};
|
||||
}
|
||||
|
||||
function createProviderManifest() {
|
||||
return {
|
||||
setup: { providers: [{ id: "demo", envVars: ["DEMO_API_KEY"] }] },
|
||||
providerAuthChoices: [
|
||||
{
|
||||
provider: "demo",
|
||||
method: "api-key",
|
||||
choiceId: "demo-api-key",
|
||||
choiceLabel: "Demo API key",
|
||||
choiceHint: "Manifest-owned key",
|
||||
groupId: "demo-group",
|
||||
groupLabel: "Demo providers",
|
||||
groupHint: "Manifest-owned setup",
|
||||
optionKey: "demoApiKey",
|
||||
cliFlag: "--demo-api-key",
|
||||
},
|
||||
],
|
||||
modelCatalog: {
|
||||
providers: {
|
||||
demo: {
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://api.demo.test/v1",
|
||||
defaultModel: "default",
|
||||
models: [createModel("default", "Default")],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createCatalogContext(
|
||||
config: ProviderCatalogContext["config"] = {},
|
||||
): ProviderCatalogContext {
|
||||
@@ -55,6 +86,116 @@ async function captureProviderEntry(params: {
|
||||
}
|
||||
|
||||
describe("defineSingleProviderPluginEntry", () => {
|
||||
it("derives API-key auth and static and live model catalogs from the provider manifest", async () => {
|
||||
const manifest = createProviderManifest();
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
name: "Demo Provider",
|
||||
description: "Demo provider plugin",
|
||||
manifest,
|
||||
provider: {
|
||||
label: "Demo",
|
||||
docsPath: "/providers/demo",
|
||||
aliases: ["demo-alias"],
|
||||
catalog: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { provider, catalog, staticCatalog, unifiedCatalog, unifiedStaticCatalog } =
|
||||
await captureProviderEntry({ entry });
|
||||
|
||||
expect(provider).toMatchObject({
|
||||
id: "demo",
|
||||
label: "Demo",
|
||||
aliases: ["demo-alias"],
|
||||
envVars: ["DEMO_API_KEY"],
|
||||
});
|
||||
expect(provider?.auth[0]).toMatchObject({
|
||||
id: "api-key",
|
||||
label: "Demo API key",
|
||||
hint: "Manifest-owned key",
|
||||
starterModel: "demo/default",
|
||||
wizard: {
|
||||
choiceId: "demo-api-key",
|
||||
choiceLabel: "Demo API key",
|
||||
choiceHint: "Manifest-owned key",
|
||||
groupId: "demo-group",
|
||||
groupLabel: "Demo providers",
|
||||
groupHint: "Manifest-owned setup",
|
||||
methodId: "api-key",
|
||||
},
|
||||
});
|
||||
const { defaultModel, ...manifestProvider } = manifest.modelCatalog.providers.demo;
|
||||
expect(defaultModel).toBe("default");
|
||||
expect(catalog).toEqual({ provider: { ...manifestProvider, apiKey: "test-key" } });
|
||||
expect(staticCatalog).toEqual({ provider: manifestProvider });
|
||||
expect(unifiedCatalog).toEqual([
|
||||
{ kind: "text", provider: "demo", model: "default", label: "Default", source: "live" },
|
||||
]);
|
||||
expect(unifiedStaticCatalog).toEqual([
|
||||
{ kind: "text", provider: "demo", model: "default", label: "Default", source: "static" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("honors explicit base URLs and provider-owned manifest auth overrides", async () => {
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
name: "Demo Provider",
|
||||
description: "Demo provider plugin",
|
||||
manifest: createProviderManifest(),
|
||||
provider: {
|
||||
label: "Demo",
|
||||
docsPath: "/providers/demo",
|
||||
manifestAuth: { defaultModel: "demo/custom", preserveExistingPrimary: true },
|
||||
catalog: { allowExplicitBaseUrl: true },
|
||||
},
|
||||
});
|
||||
|
||||
const { provider, catalog, staticCatalog } = await captureProviderEntry({
|
||||
entry,
|
||||
config: {
|
||||
models: {
|
||||
providers: {
|
||||
demo: {
|
||||
baseUrl: "https://override.demo.test/v1",
|
||||
models: [createModel("configured", "Configured")],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(provider?.auth[0]?.starterModel).toBe("demo/custom");
|
||||
expect(catalog).toMatchObject({ provider: { baseUrl: "https://override.demo.test/v1" } });
|
||||
expect(staticCatalog).toMatchObject({ provider: { baseUrl: "https://api.demo.test/v1" } });
|
||||
});
|
||||
|
||||
it("rejects manifest API-key metadata without its declared credential source", () => {
|
||||
const manifest = createProviderManifest();
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
name: "Demo Provider",
|
||||
description: "Demo provider plugin",
|
||||
manifest: { ...manifest, setup: { providers: [] } },
|
||||
provider: { label: "Demo", docsPath: "/providers/demo", catalog: {} },
|
||||
});
|
||||
|
||||
expect(() => capturePluginRegistration(entry)).toThrow(
|
||||
'Incomplete manifest API-key auth for provider "demo"',
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects a provider catalog without a manifest catalog or explicit builder", () => {
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
name: "Demo Provider",
|
||||
description: "Demo provider plugin",
|
||||
provider: { label: "Demo", docsPath: "/providers/demo", catalog: {} },
|
||||
});
|
||||
|
||||
expect(() => capturePluginRegistration(entry)).toThrow("Missing modelCatalog.providers.demo");
|
||||
});
|
||||
|
||||
it("registers a single provider with default wizard metadata", async () => {
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
|
||||
@@ -4,6 +4,10 @@ import {
|
||||
normalizeStringEntries,
|
||||
uniqueStrings,
|
||||
} from "../../packages/normalization-core/src/string-normalization.js";
|
||||
import type {
|
||||
PluginManifestProviderAuthChoice,
|
||||
PluginManifestSetupProvider,
|
||||
} from "../plugins/manifest-types.js";
|
||||
import { createProviderApiKeyAuthMethod } from "../plugins/provider-api-key-auth.js";
|
||||
import { projectProviderCatalogResultToUnifiedTextRows } from "../plugins/provider-catalog-unified-text.js";
|
||||
import type {
|
||||
@@ -30,10 +34,25 @@ import {
|
||||
buildOpenAICompatibleProviderCatalog,
|
||||
type OpenAICompatibleModelDiscoveryOptions,
|
||||
} from "./provider-catalog-live-runtime.js";
|
||||
import { buildSingleProviderApiKeyCatalog } from "./provider-catalog-shared.js";
|
||||
import {
|
||||
buildManifestModelProviderConfig,
|
||||
buildSingleProviderApiKeyCatalog,
|
||||
readManifestProviderDefaultModelRef,
|
||||
} from "./provider-catalog-shared.js";
|
||||
|
||||
type ApiKeyAuthMethodOptions = Parameters<typeof createProviderApiKeyAuthMethod>[0];
|
||||
|
||||
type SingleProviderPluginManifest = {
|
||||
setup?: {
|
||||
providers?: readonly Pick<PluginManifestSetupProvider, "id" | "envVars">[];
|
||||
};
|
||||
providerAuthChoices?: readonly PluginManifestProviderAuthChoice[];
|
||||
modelCatalog?: {
|
||||
providers?: Readonly<Record<string, unknown>>;
|
||||
discovery?: Readonly<Record<string, unknown>>;
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* API-key auth options for single-provider plugins, with provider id filled in by the entry helper.
|
||||
*/
|
||||
@@ -53,6 +72,13 @@ export type SingleProviderPluginApiKeyAuthOptions = Omit<
|
||||
wizard?: false | ProviderPluginWizardSetup;
|
||||
};
|
||||
|
||||
type ManifestProviderAuthOptions = Omit<
|
||||
SingleProviderPluginApiKeyAuthOptions,
|
||||
"methodId" | "label" | "optionKey" | "flagName" | "envVar" | "promptMessage"
|
||||
> & {
|
||||
promptMessage?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Catalog configuration accepted by the single-provider entry helper.
|
||||
*/
|
||||
@@ -61,7 +87,7 @@ export type SingleProviderPluginCatalogOptions =
|
||||
/**
|
||||
* Builds the live provider catalog through the shared API-key catalog path.
|
||||
*/
|
||||
buildProvider: Parameters<typeof buildSingleProviderApiKeyCatalog>[0]["buildProvider"];
|
||||
buildProvider?: Parameters<typeof buildSingleProviderApiKeyCatalog>[0]["buildProvider"];
|
||||
/**
|
||||
* Builds a static catalog for cheap model discovery before credentials are resolved.
|
||||
*/
|
||||
@@ -113,6 +139,11 @@ export type SingleProviderPluginOptions = {
|
||||
* Short plugin description surfaced by plugin registries and setup flows.
|
||||
*/
|
||||
description: string;
|
||||
/**
|
||||
* Plugin-owned metadata used to derive API-key auth and model catalogs
|
||||
* without repeating the manifest in the runtime entry.
|
||||
*/
|
||||
manifest?: SingleProviderPluginManifest;
|
||||
/**
|
||||
* @deprecated Declare exclusive plugin kind in `openclaw.plugin.json` via
|
||||
* manifest `kind`. Runtime-entry `kind` remains only as a compatibility
|
||||
@@ -152,6 +183,10 @@ export type SingleProviderPluginOptions = {
|
||||
* API-key auth methods converted through the shared provider auth helper.
|
||||
*/
|
||||
auth?: SingleProviderPluginApiKeyAuthOptions[];
|
||||
/**
|
||||
* Provider-owned behavior layered over manifest-derived API-key auth.
|
||||
*/
|
||||
manifestAuth?: ManifestProviderAuthOptions;
|
||||
/**
|
||||
* Non-API-key auth methods appended after generated API-key methods.
|
||||
*/
|
||||
@@ -170,6 +205,50 @@ export type SingleProviderPluginOptions = {
|
||||
register?: (api: OpenClawPluginApi) => void;
|
||||
};
|
||||
|
||||
function resolveManifestProviderAuth(params: {
|
||||
manifest: SingleProviderPluginManifest | undefined;
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
overrides?: ManifestProviderAuthOptions;
|
||||
}): SingleProviderPluginApiKeyAuthOptions[] {
|
||||
const choice = params.manifest?.providerAuthChoices?.find(
|
||||
(entry) => entry.provider === params.providerId && entry.method === "api-key",
|
||||
);
|
||||
if (!choice) {
|
||||
return [];
|
||||
}
|
||||
const envVar = params.manifest?.setup?.providers?.find((entry) => entry.id === params.providerId)
|
||||
?.envVars?.[0];
|
||||
if (!choice.choiceLabel || !choice.optionKey || !choice.cliFlag?.startsWith("--") || !envVar) {
|
||||
throw new Error(`Incomplete manifest API-key auth for provider "${params.providerId}"`);
|
||||
}
|
||||
const defaultModel = readManifestProviderDefaultModelRef(params.manifest, params.providerId);
|
||||
return [
|
||||
{
|
||||
methodId: choice.method,
|
||||
label: choice.choiceLabel,
|
||||
...(choice.choiceHint || choice.groupHint
|
||||
? { hint: choice.choiceHint ?? choice.groupHint }
|
||||
: {}),
|
||||
optionKey: choice.optionKey,
|
||||
flagName: choice.cliFlag as `--${string}`,
|
||||
envVar,
|
||||
promptMessage: `Enter ${choice.choiceLabel}`,
|
||||
...(defaultModel ? { defaultModel } : {}),
|
||||
wizard: {
|
||||
choiceId: choice.choiceId,
|
||||
choiceLabel: choice.choiceLabel,
|
||||
groupId: choice.groupId ?? params.providerId,
|
||||
groupLabel: choice.groupLabel ?? params.providerLabel,
|
||||
...(choice.choiceHint ? { choiceHint: choice.choiceHint } : {}),
|
||||
...(choice.groupHint ? { groupHint: choice.groupHint } : {}),
|
||||
methodId: choice.method,
|
||||
},
|
||||
...params.overrides,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function resolveWizardSetup(params: {
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
@@ -244,7 +323,22 @@ export function defineSingleProviderPluginEntry(options: SingleProviderPluginOpt
|
||||
const provider = options.provider;
|
||||
if (provider) {
|
||||
const providerId = provider.id ?? options.id;
|
||||
const providerAuth = copyProviderAuthOptions(provider.auth);
|
||||
if (
|
||||
!("run" in provider.catalog) &&
|
||||
!provider.catalog.buildProvider &&
|
||||
!options.manifest?.modelCatalog?.providers?.[providerId]
|
||||
) {
|
||||
throw new Error(`Missing modelCatalog.providers.${providerId}`);
|
||||
}
|
||||
const providerAuth = copyProviderAuthOptions(
|
||||
provider.auth ??
|
||||
resolveManifestProviderAuth({
|
||||
manifest: options.manifest,
|
||||
providerId,
|
||||
providerLabel: provider.label,
|
||||
overrides: provider.manifestAuth,
|
||||
}),
|
||||
);
|
||||
const acceptedProviderAuth: SingleProviderPluginApiKeyAuthOptions[] = [];
|
||||
const auth = providerAuth.flatMap((entry) => {
|
||||
try {
|
||||
@@ -281,7 +375,13 @@ export function defineSingleProviderPluginEntry(options: SingleProviderPluginOpt
|
||||
run: catalogRun!,
|
||||
};
|
||||
} else {
|
||||
const buildProvider = provider.catalog.buildProvider;
|
||||
const buildProvider =
|
||||
provider.catalog.buildProvider ??
|
||||
(() =>
|
||||
buildManifestModelProviderConfig({
|
||||
providerId,
|
||||
catalog: options.manifest?.modelCatalog?.providers?.[providerId],
|
||||
}));
|
||||
catalog = {
|
||||
order: "simple",
|
||||
run: (ctx: ProviderCatalogContext): Promise<ProviderCatalogResult> =>
|
||||
@@ -307,6 +407,17 @@ export function defineSingleProviderPluginEntry(options: SingleProviderPluginOpt
|
||||
}),
|
||||
};
|
||||
}
|
||||
const manifestStaticProvider =
|
||||
"run" in provider.catalog
|
||||
? undefined
|
||||
: (provider.catalog.buildStaticProvider ??
|
||||
(provider.catalog.buildProvider
|
||||
? undefined
|
||||
: () =>
|
||||
buildManifestModelProviderConfig({
|
||||
providerId,
|
||||
catalog: options.manifest?.modelCatalog?.providers?.[providerId],
|
||||
})));
|
||||
const staticCatalog: ProviderPluginCatalog | undefined =
|
||||
"run" in provider.catalog
|
||||
? provider.catalog.staticRun
|
||||
@@ -315,11 +426,11 @@ export function defineSingleProviderPluginEntry(options: SingleProviderPluginOpt
|
||||
run: provider.catalog.staticRun,
|
||||
}
|
||||
: undefined
|
||||
: provider.catalog.buildStaticProvider
|
||||
: manifestStaticProvider
|
||||
? {
|
||||
order: "simple",
|
||||
run: async () => ({
|
||||
provider: await provider.catalog.buildStaticProvider!(),
|
||||
provider: await manifestStaticProvider(),
|
||||
}),
|
||||
}
|
||||
: undefined;
|
||||
@@ -344,6 +455,7 @@ export function defineSingleProviderPluginEntry(options: SingleProviderPluginOpt
|
||||
"aliases",
|
||||
"envVars",
|
||||
"auth",
|
||||
"manifestAuth",
|
||||
"extraAuth",
|
||||
"catalog",
|
||||
"staticCatalog",
|
||||
|
||||
Reference in New Issue
Block a user