mirror of https://github.com/openclaw/openclaw.git
refactor: extract single-provider plugin entry helper
This commit is contained in:
parent
6237cfc6a6
commit
956fe72b39
|
|
@ -148,6 +148,43 @@ API key auth, and dynamic model resolution.
|
|||
`openclaw onboard --acme-ai-api-key <key>` and select
|
||||
`acme-ai/acme-large` as their model.
|
||||
|
||||
For bundled providers that only register one text provider with API-key
|
||||
auth plus a single catalog-backed runtime, prefer the narrower
|
||||
`defineSingleProviderPluginEntry(...)` helper:
|
||||
|
||||
```typescript
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: "acme-ai",
|
||||
name: "Acme AI",
|
||||
description: "Acme AI model provider",
|
||||
provider: {
|
||||
label: "Acme AI",
|
||||
docsPath: "/providers/acme-ai",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Acme AI API key",
|
||||
hint: "API key from your Acme AI dashboard",
|
||||
optionKey: "acmeAiApiKey",
|
||||
flagName: "--acme-ai-api-key",
|
||||
envVar: "ACME_AI_API_KEY",
|
||||
promptMessage: "Enter your Acme AI API key",
|
||||
defaultModel: "acme-ai/acme-large",
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: () => ({
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://api.acme-ai.com/v1",
|
||||
models: [{ id: "acme-large", name: "Acme Large" }],
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Add dynamic model resolution">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import {
|
||||
createKilocodeWrapper,
|
||||
isProxyReasoningUnsupported,
|
||||
|
|
@ -10,59 +8,40 @@ import { buildKilocodeProviderWithDiscovery } from "./provider-catalog.js";
|
|||
|
||||
const PROVIDER_ID = "kilocode";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Kilo Gateway Provider",
|
||||
description: "Bundled Kilo Gateway provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Kilo Gateway",
|
||||
docsPath: "/providers/kilocode",
|
||||
envVars: ["KILOCODE_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["kilocode"],
|
||||
applyConfig: (cfg) => applyKilocodeConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "kilocode-api-key",
|
||||
choiceLabel: "Kilo Gateway API key",
|
||||
groupId: "kilocode",
|
||||
groupLabel: "Kilo Gateway",
|
||||
groupHint: "API key (OpenRouter-compatible)",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildKilocodeProviderWithDiscovery,
|
||||
}),
|
||||
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),
|
||||
},
|
||||
capabilities: {
|
||||
geminiThoughtSignatureSanitization: true,
|
||||
geminiThoughtSignatureModelHints: ["gemini"],
|
||||
},
|
||||
wrapStreamFn: (ctx) => {
|
||||
const thinkingLevel =
|
||||
ctx.modelId === "kilo/auto" || isProxyReasoningUnsupported(ctx.modelId)
|
||||
? undefined
|
||||
: ctx.thinkingLevel;
|
||||
return createKilocodeWrapper(ctx.streamFn, thinkingLevel);
|
||||
},
|
||||
isCacheTtlEligible: (ctx) => ctx.modelId.startsWith("anthropic/"),
|
||||
});
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildKilocodeProviderWithDiscovery,
|
||||
},
|
||||
capabilities: {
|
||||
geminiThoughtSignatureSanitization: true,
|
||||
geminiThoughtSignatureModelHints: ["gemini"],
|
||||
},
|
||||
wrapStreamFn: (ctx) => {
|
||||
const thinkingLevel =
|
||||
ctx.modelId === "kilo/auto" || isProxyReasoningUnsupported(ctx.modelId)
|
||||
? undefined
|
||||
: ctx.thinkingLevel;
|
||||
return createKilocodeWrapper(ctx.streamFn, thinkingLevel);
|
||||
},
|
||||
isCacheTtlEligible: (ctx) => ctx.modelId.startsWith("anthropic/"),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,67 +1,51 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { mistralMediaUnderstandingProvider } from "./media-understanding-provider.js";
|
||||
import { applyMistralConfig, MISTRAL_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildMistralProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "mistral";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Mistral Provider",
|
||||
description: "Bundled Mistral provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Mistral",
|
||||
docsPath: "/providers/models",
|
||||
envVars: ["MISTRAL_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["mistral"],
|
||||
applyConfig: (cfg) => applyMistralConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "mistral-api-key",
|
||||
choiceLabel: "Mistral API key",
|
||||
groupId: "mistral",
|
||||
groupLabel: "Mistral AI",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
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",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildMistralProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
},
|
||||
capabilities: {
|
||||
transcriptToolCallIdMode: "strict9",
|
||||
transcriptToolCallIdModelHints: [
|
||||
"mistral",
|
||||
"mixtral",
|
||||
"codestral",
|
||||
"pixtral",
|
||||
"devstral",
|
||||
"ministral",
|
||||
"mistralai",
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildMistralProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
}),
|
||||
},
|
||||
capabilities: {
|
||||
transcriptToolCallIdMode: "strict9",
|
||||
transcriptToolCallIdModelHints: [
|
||||
"mistral",
|
||||
"mixtral",
|
||||
"codestral",
|
||||
"pixtral",
|
||||
"devstral",
|
||||
"ministral",
|
||||
"mistralai",
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
},
|
||||
register(api) {
|
||||
api.registerMediaUnderstandingProvider(mistralMediaUnderstandingProvider);
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import {
|
||||
applyModelStudioConfig,
|
||||
applyModelStudioConfigCn,
|
||||
|
|
@ -10,82 +8,62 @@ import { buildModelStudioProvider } from "./provider-catalog.js";
|
|||
|
||||
const PROVIDER_ID = "modelstudio";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Model Studio Provider",
|
||||
description: "Bundled Model Studio provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Model Studio",
|
||||
docsPath: "/providers/models",
|
||||
envVars: ["MODELSTUDIO_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key-cn",
|
||||
label: "Coding Plan API Key for China (subscription)",
|
||||
hint: "Endpoint: coding.dashscope.aliyuncs.com",
|
||||
optionKey: "modelstudioApiKeyCn",
|
||||
flagName: "--modelstudio-api-key-cn",
|
||||
envVar: "MODELSTUDIO_API_KEY",
|
||||
promptMessage: "Enter Alibaba Cloud Model Studio Coding Plan API key (China)",
|
||||
defaultModel: MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["modelstudio"],
|
||||
applyConfig: (cfg) => applyModelStudioConfigCn(cfg),
|
||||
noteMessage: [
|
||||
"Get your API key at: https://bailian.console.aliyun.com/",
|
||||
"Endpoint: coding.dashscope.aliyuncs.com",
|
||||
"Models: qwen3.5-plus, glm-4.7, kimi-k2.5, MiniMax-M2.5, etc.",
|
||||
].join("\n"),
|
||||
noteTitle: "Alibaba Cloud Model Studio Coding Plan (China)",
|
||||
wizard: {
|
||||
choiceId: "modelstudio-api-key-cn",
|
||||
choiceLabel: "Coding Plan API Key for China (subscription)",
|
||||
choiceHint: "Endpoint: coding.dashscope.aliyuncs.com",
|
||||
groupId: "modelstudio",
|
||||
groupLabel: "Alibaba Cloud Model Studio",
|
||||
groupHint: "Coding Plan API key (CN / Global)",
|
||||
},
|
||||
}),
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "Coding Plan API Key for Global/Intl (subscription)",
|
||||
hint: "Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
optionKey: "modelstudioApiKey",
|
||||
flagName: "--modelstudio-api-key",
|
||||
envVar: "MODELSTUDIO_API_KEY",
|
||||
promptMessage: "Enter Alibaba Cloud Model Studio Coding Plan API key (Global/Intl)",
|
||||
defaultModel: MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["modelstudio"],
|
||||
applyConfig: (cfg) => applyModelStudioConfig(cfg),
|
||||
noteMessage: [
|
||||
"Get your API key at: https://bailian.console.aliyun.com/",
|
||||
"Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
"Models: qwen3.5-plus, glm-4.7, kimi-k2.5, MiniMax-M2.5, etc.",
|
||||
].join("\n"),
|
||||
noteTitle: "Alibaba Cloud Model Studio Coding Plan (Global/Intl)",
|
||||
wizard: {
|
||||
choiceId: "modelstudio-api-key",
|
||||
choiceLabel: "Coding Plan API Key for Global/Intl (subscription)",
|
||||
choiceHint: "Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
groupId: "modelstudio",
|
||||
groupLabel: "Alibaba Cloud Model Studio",
|
||||
groupHint: "Coding Plan API key (CN / Global)",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildModelStudioProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
}),
|
||||
provider: {
|
||||
label: "Model Studio",
|
||||
docsPath: "/providers/models",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key-cn",
|
||||
label: "Coding Plan API Key for China (subscription)",
|
||||
hint: "Endpoint: coding.dashscope.aliyuncs.com",
|
||||
optionKey: "modelstudioApiKeyCn",
|
||||
flagName: "--modelstudio-api-key-cn",
|
||||
envVar: "MODELSTUDIO_API_KEY",
|
||||
promptMessage: "Enter Alibaba Cloud Model Studio Coding Plan API key (China)",
|
||||
defaultModel: MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyModelStudioConfigCn(cfg),
|
||||
noteMessage: [
|
||||
"Get your API key at: https://bailian.console.aliyun.com/",
|
||||
"Endpoint: coding.dashscope.aliyuncs.com",
|
||||
"Models: qwen3.5-plus, glm-4.7, kimi-k2.5, MiniMax-M2.5, etc.",
|
||||
].join("\n"),
|
||||
noteTitle: "Alibaba Cloud Model Studio Coding Plan (China)",
|
||||
wizard: {
|
||||
choiceHint: "Endpoint: coding.dashscope.aliyuncs.com",
|
||||
groupLabel: "Alibaba Cloud Model Studio",
|
||||
groupHint: "Coding Plan API key (CN / Global)",
|
||||
},
|
||||
},
|
||||
});
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Coding Plan API Key for Global/Intl (subscription)",
|
||||
hint: "Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
optionKey: "modelstudioApiKey",
|
||||
flagName: "--modelstudio-api-key",
|
||||
envVar: "MODELSTUDIO_API_KEY",
|
||||
promptMessage: "Enter Alibaba Cloud Model Studio Coding Plan API key (Global/Intl)",
|
||||
defaultModel: MODELSTUDIO_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyModelStudioConfig(cfg),
|
||||
noteMessage: [
|
||||
"Get your API key at: https://bailian.console.aliyun.com/",
|
||||
"Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
"Models: qwen3.5-plus, glm-4.7, kimi-k2.5, MiniMax-M2.5, etc.",
|
||||
].join("\n"),
|
||||
noteTitle: "Alibaba Cloud Model Studio Coding Plan (Global/Intl)",
|
||||
wizard: {
|
||||
choiceHint: "Endpoint: coding-intl.dashscope.aliyuncs.com",
|
||||
groupLabel: "Alibaba Cloud Model Studio",
|
||||
groupHint: "Coding Plan API key (CN / Global)",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildModelStudioProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import {
|
||||
createMoonshotThinkingWrapper,
|
||||
resolveMoonshotThinkingType,
|
||||
|
|
@ -16,76 +14,56 @@ import { createKimiWebSearchProvider } from "./src/kimi-web-search-provider.js";
|
|||
|
||||
const PROVIDER_ID = "moonshot";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Moonshot Provider",
|
||||
description: "Bundled Moonshot provider plugin",
|
||||
provider: {
|
||||
label: "Moonshot",
|
||||
docsPath: "/providers/moonshot",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Kimi API key (.ai)",
|
||||
hint: "Kimi K2.5 + Kimi",
|
||||
optionKey: "moonshotApiKey",
|
||||
flagName: "--moonshot-api-key",
|
||||
envVar: "MOONSHOT_API_KEY",
|
||||
promptMessage: "Enter Moonshot API key",
|
||||
defaultModel: MOONSHOT_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyMoonshotConfig(cfg),
|
||||
wizard: {
|
||||
groupLabel: "Moonshot AI (Kimi K2.5)",
|
||||
},
|
||||
},
|
||||
{
|
||||
methodId: "api-key-cn",
|
||||
label: "Kimi API key (.cn)",
|
||||
hint: "Kimi K2.5 + Kimi",
|
||||
optionKey: "moonshotApiKey",
|
||||
flagName: "--moonshot-api-key",
|
||||
envVar: "MOONSHOT_API_KEY",
|
||||
promptMessage: "Enter Moonshot API key (.cn)",
|
||||
defaultModel: MOONSHOT_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyMoonshotConfigCn(cfg),
|
||||
wizard: {
|
||||
groupLabel: "Moonshot AI (Kimi K2.5)",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildMoonshotProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
},
|
||||
wrapStreamFn: (ctx) => {
|
||||
const thinkingType = resolveMoonshotThinkingType({
|
||||
configuredThinking: ctx.extraParams?.thinking,
|
||||
thinkingLevel: ctx.thinkingLevel,
|
||||
});
|
||||
return createMoonshotThinkingWrapper(ctx.streamFn, thinkingType);
|
||||
},
|
||||
},
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Moonshot",
|
||||
docsPath: "/providers/moonshot",
|
||||
envVars: ["MOONSHOT_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "Kimi API key (.ai)",
|
||||
hint: "Kimi K2.5 + Kimi",
|
||||
optionKey: "moonshotApiKey",
|
||||
flagName: "--moonshot-api-key",
|
||||
envVar: "MOONSHOT_API_KEY",
|
||||
promptMessage: "Enter Moonshot API key",
|
||||
defaultModel: MOONSHOT_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["moonshot"],
|
||||
applyConfig: (cfg) => applyMoonshotConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "moonshot-api-key",
|
||||
choiceLabel: "Kimi API key (.ai)",
|
||||
groupId: "moonshot",
|
||||
groupLabel: "Moonshot AI (Kimi K2.5)",
|
||||
groupHint: "Kimi K2.5 + Kimi",
|
||||
},
|
||||
}),
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key-cn",
|
||||
label: "Kimi API key (.cn)",
|
||||
hint: "Kimi K2.5 + Kimi",
|
||||
optionKey: "moonshotApiKey",
|
||||
flagName: "--moonshot-api-key",
|
||||
envVar: "MOONSHOT_API_KEY",
|
||||
promptMessage: "Enter Moonshot API key (.cn)",
|
||||
defaultModel: MOONSHOT_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["moonshot"],
|
||||
applyConfig: (cfg) => applyMoonshotConfigCn(cfg),
|
||||
wizard: {
|
||||
choiceId: "moonshot-api-key-cn",
|
||||
choiceLabel: "Kimi API key (.cn)",
|
||||
groupId: "moonshot",
|
||||
groupLabel: "Moonshot AI (Kimi K2.5)",
|
||||
groupHint: "Kimi K2.5 + Kimi",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildMoonshotProvider,
|
||||
allowExplicitBaseUrl: true,
|
||||
}),
|
||||
},
|
||||
wrapStreamFn: (ctx) => {
|
||||
const thinkingType = resolveMoonshotThinkingType({
|
||||
configuredThinking: ctx.extraParams?.thinking,
|
||||
thinkingLevel: ctx.thinkingLevel,
|
||||
});
|
||||
return createMoonshotThinkingWrapper(ctx.streamFn, thinkingType);
|
||||
},
|
||||
});
|
||||
api.registerMediaUnderstandingProvider(moonshotMediaUnderstandingProvider);
|
||||
api.registerWebSearchProvider(createKimiWebSearchProvider());
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,29 +1,19 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { buildNvidiaProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "nvidia";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "NVIDIA Provider",
|
||||
description: "Bundled NVIDIA provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "NVIDIA",
|
||||
docsPath: "/providers/nvidia",
|
||||
envVars: ["NVIDIA_API_KEY"],
|
||||
auth: [],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildNvidiaProvider,
|
||||
}),
|
||||
},
|
||||
});
|
||||
provider: {
|
||||
label: "NVIDIA",
|
||||
docsPath: "/providers/nvidia",
|
||||
envVars: ["NVIDIA_API_KEY"],
|
||||
auth: [],
|
||||
catalog: {
|
||||
buildProvider: buildNvidiaProvider,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,52 +1,31 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyQianfanConfig, QIANFAN_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildQianfanProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "qianfan";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Qianfan Provider",
|
||||
description: "Bundled Qianfan provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Qianfan",
|
||||
docsPath: "/providers/qianfan",
|
||||
envVars: ["QIANFAN_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["qianfan"],
|
||||
applyConfig: (cfg) => applyQianfanConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "qianfan-api-key",
|
||||
choiceLabel: "Qianfan API key",
|
||||
groupId: "qianfan",
|
||||
groupLabel: "Qianfan",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildQianfanProvider,
|
||||
}),
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,52 +1,31 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applySyntheticConfig, SYNTHETIC_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildSyntheticProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "synthetic";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Synthetic Provider",
|
||||
description: "Bundled Synthetic provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Synthetic",
|
||||
docsPath: "/providers/synthetic",
|
||||
envVars: ["SYNTHETIC_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["synthetic"],
|
||||
applyConfig: (cfg) => applySyntheticConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "synthetic-api-key",
|
||||
choiceLabel: "Synthetic API key",
|
||||
groupId: "synthetic",
|
||||
groupLabel: "Synthetic",
|
||||
groupHint: "Anthropic-compatible (multi-model)",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildSyntheticProvider,
|
||||
}),
|
||||
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),
|
||||
},
|
||||
});
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildSyntheticProvider,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,52 +1,34 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyTogetherConfig, TOGETHER_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildTogetherProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "together";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Together Provider",
|
||||
description: "Bundled Together provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Together",
|
||||
docsPath: "/providers/together",
|
||||
envVars: ["TOGETHER_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["together"],
|
||||
applyConfig: (cfg) => applyTogetherConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "together-api-key",
|
||||
choiceLabel: "Together AI API key",
|
||||
groupId: "together",
|
||||
groupLabel: "Together AI",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildTogetherProvider,
|
||||
}),
|
||||
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,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyXaiModelCompat } from "openclaw/plugin-sdk/provider-models";
|
||||
import { applyVeniceConfig, VENICE_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildVeniceProvider } from "./provider-catalog.js";
|
||||
|
|
@ -11,55 +9,39 @@ function isXaiBackedVeniceModel(modelId: string): boolean {
|
|||
return modelId.trim().toLowerCase().includes("grok");
|
||||
}
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Venice Provider",
|
||||
description: "Bundled Venice provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Venice",
|
||||
docsPath: "/providers/venice",
|
||||
envVars: ["VENICE_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["venice"],
|
||||
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: {
|
||||
choiceId: "venice-api-key",
|
||||
choiceLabel: "Venice AI API key",
|
||||
groupId: "venice",
|
||||
groupLabel: "Venice AI",
|
||||
groupHint: "Privacy-focused (uncensored models)",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildVeniceProvider,
|
||||
}),
|
||||
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",
|
||||
},
|
||||
},
|
||||
normalizeResolvedModel: ({ modelId, model }) =>
|
||||
isXaiBackedVeniceModel(modelId) ? applyXaiModelCompat(model) : undefined,
|
||||
});
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildVeniceProvider,
|
||||
},
|
||||
normalizeResolvedModel: ({ modelId, model }) =>
|
||||
isXaiBackedVeniceModel(modelId) ? applyXaiModelCompat(model) : undefined,
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,52 +1,35 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyVercelAiGatewayConfig, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildVercelAiGatewayProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "vercel-ai-gateway";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Vercel AI Gateway Provider",
|
||||
description: "Bundled Vercel AI Gateway provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Vercel AI Gateway",
|
||||
docsPath: "/providers/vercel-ai-gateway",
|
||||
envVars: ["AI_GATEWAY_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
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,
|
||||
expectedProviders: ["vercel-ai-gateway"],
|
||||
applyConfig: (cfg) => applyVercelAiGatewayConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "ai-gateway-api-key",
|
||||
choiceLabel: "Vercel AI Gateway API key",
|
||||
groupId: "ai-gateway",
|
||||
groupLabel: "Vercel AI Gateway",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildVercelAiGatewayProvider,
|
||||
}),
|
||||
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",
|
||||
},
|
||||
},
|
||||
});
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildVercelAiGatewayProvider,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth-api-key";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { applyXaiModelCompat } from "openclaw/plugin-sdk/provider-models";
|
||||
import { createToolStreamWrapper } from "openclaw/plugin-sdk/provider-stream";
|
||||
import { applyXaiConfig, XAI_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
|
|
@ -14,68 +12,54 @@ import { createXaiWebSearchProvider } from "./web-search.js";
|
|||
|
||||
const PROVIDER_ID = "xai";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: "xai",
|
||||
name: "xAI Plugin",
|
||||
description: "Bundled xAI plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "xAI",
|
||||
aliases: ["x-ai"],
|
||||
docsPath: "/providers/xai",
|
||||
envVars: ["XAI_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "xAI API key",
|
||||
hint: "API key",
|
||||
optionKey: "xaiApiKey",
|
||||
flagName: "--xai-api-key",
|
||||
envVar: "XAI_API_KEY",
|
||||
promptMessage: "Enter xAI API key",
|
||||
defaultModel: XAI_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["xai"],
|
||||
applyConfig: (cfg) => applyXaiConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "xai-api-key",
|
||||
choiceLabel: "xAI API key",
|
||||
groupId: "xai",
|
||||
groupLabel: "xAI (Grok)",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildXaiProvider,
|
||||
}),
|
||||
provider: {
|
||||
label: "xAI",
|
||||
aliases: ["x-ai"],
|
||||
docsPath: "/providers/xai",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "xAI API key",
|
||||
hint: "API key",
|
||||
optionKey: "xaiApiKey",
|
||||
flagName: "--xai-api-key",
|
||||
envVar: "XAI_API_KEY",
|
||||
promptMessage: "Enter xAI API key",
|
||||
defaultModel: XAI_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyXaiConfig(cfg),
|
||||
wizard: {
|
||||
groupLabel: "xAI (Grok)",
|
||||
},
|
||||
},
|
||||
prepareExtraParams: (ctx) => {
|
||||
if (ctx.extraParams?.tool_stream !== undefined) {
|
||||
return ctx.extraParams;
|
||||
}
|
||||
return {
|
||||
...ctx.extraParams,
|
||||
tool_stream: true,
|
||||
};
|
||||
},
|
||||
wrapStreamFn: (ctx) =>
|
||||
createToolStreamWrapper(
|
||||
createXaiToolCallArgumentDecodingWrapper(
|
||||
createXaiToolPayloadCompatibilityWrapper(ctx.streamFn),
|
||||
),
|
||||
ctx.extraParams?.tool_stream !== false,
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildXaiProvider,
|
||||
},
|
||||
prepareExtraParams: (ctx) => {
|
||||
if (ctx.extraParams?.tool_stream !== undefined) {
|
||||
return ctx.extraParams;
|
||||
}
|
||||
return {
|
||||
...ctx.extraParams,
|
||||
tool_stream: true,
|
||||
};
|
||||
},
|
||||
wrapStreamFn: (ctx) =>
|
||||
createToolStreamWrapper(
|
||||
createXaiToolCallArgumentDecodingWrapper(
|
||||
createXaiToolPayloadCompatibilityWrapper(ctx.streamFn),
|
||||
),
|
||||
normalizeResolvedModel: ({ model }) => applyXaiModelCompat(model),
|
||||
resolveDynamicModel: (ctx) => resolveXaiForwardCompatModel({ providerId: PROVIDER_ID, ctx }),
|
||||
isModernModelRef: ({ modelId }) => isModernXaiModel(modelId),
|
||||
});
|
||||
ctx.extraParams?.tool_stream !== false,
|
||||
),
|
||||
normalizeResolvedModel: ({ model }) => applyXaiModelCompat(model),
|
||||
resolveDynamicModel: (ctx) => resolveXaiForwardCompatModel({ providerId: PROVIDER_ID, ctx }),
|
||||
isModernModelRef: ({ modelId }) => isModernXaiModel(modelId),
|
||||
},
|
||||
register(api) {
|
||||
api.registerWebSearchProvider(createXaiWebSearchProvider());
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,64 +1,43 @@
|
|||
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
||||
import { createProviderApiKeyAuthMethod } from "openclaw/plugin-sdk/provider-auth";
|
||||
import { buildSingleProviderApiKeyCatalog } from "openclaw/plugin-sdk/provider-catalog";
|
||||
import { defineSingleProviderPluginEntry } from "openclaw/plugin-sdk/provider-entry";
|
||||
import { PROVIDER_LABELS } from "openclaw/plugin-sdk/provider-usage";
|
||||
import { applyXiaomiConfig, XIAOMI_DEFAULT_MODEL_REF } from "./onboard.js";
|
||||
import { buildXiaomiProvider } from "./provider-catalog.js";
|
||||
|
||||
const PROVIDER_ID = "xiaomi";
|
||||
|
||||
export default definePluginEntry({
|
||||
export default defineSingleProviderPluginEntry({
|
||||
id: PROVIDER_ID,
|
||||
name: "Xiaomi Provider",
|
||||
description: "Bundled Xiaomi provider plugin",
|
||||
register(api) {
|
||||
api.registerProvider({
|
||||
id: PROVIDER_ID,
|
||||
label: "Xiaomi",
|
||||
docsPath: "/providers/xiaomi",
|
||||
envVars: ["XIAOMI_API_KEY"],
|
||||
auth: [
|
||||
createProviderApiKeyAuthMethod({
|
||||
providerId: PROVIDER_ID,
|
||||
methodId: "api-key",
|
||||
label: "Xiaomi API key",
|
||||
hint: "API key",
|
||||
optionKey: "xiaomiApiKey",
|
||||
flagName: "--xiaomi-api-key",
|
||||
envVar: "XIAOMI_API_KEY",
|
||||
promptMessage: "Enter Xiaomi API key",
|
||||
defaultModel: XIAOMI_DEFAULT_MODEL_REF,
|
||||
expectedProviders: ["xiaomi"],
|
||||
applyConfig: (cfg) => applyXiaomiConfig(cfg),
|
||||
wizard: {
|
||||
choiceId: "xiaomi-api-key",
|
||||
choiceLabel: "Xiaomi API key",
|
||||
groupId: "xiaomi",
|
||||
groupLabel: "Xiaomi",
|
||||
groupHint: "API key",
|
||||
},
|
||||
}),
|
||||
],
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId: PROVIDER_ID,
|
||||
buildProvider: buildXiaomiProvider,
|
||||
}),
|
||||
provider: {
|
||||
label: "Xiaomi",
|
||||
docsPath: "/providers/xiaomi",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Xiaomi API key",
|
||||
hint: "API key",
|
||||
optionKey: "xiaomiApiKey",
|
||||
flagName: "--xiaomi-api-key",
|
||||
envVar: "XIAOMI_API_KEY",
|
||||
promptMessage: "Enter Xiaomi API key",
|
||||
defaultModel: XIAOMI_DEFAULT_MODEL_REF,
|
||||
applyConfig: (cfg) => applyXiaomiConfig(cfg),
|
||||
},
|
||||
resolveUsageAuth: async (ctx) => {
|
||||
const apiKey = ctx.resolveApiKeyFromConfigAndStore({
|
||||
envDirect: [ctx.env.XIAOMI_API_KEY],
|
||||
});
|
||||
return apiKey ? { token: apiKey } : null;
|
||||
},
|
||||
fetchUsageSnapshot: async () => ({
|
||||
provider: "xiaomi",
|
||||
displayName: PROVIDER_LABELS.xiaomi,
|
||||
windows: [],
|
||||
}),
|
||||
});
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: buildXiaomiProvider,
|
||||
},
|
||||
resolveUsageAuth: async (ctx) => {
|
||||
const apiKey = ctx.resolveApiKeyFromConfigAndStore({
|
||||
envDirect: [ctx.env.XIAOMI_API_KEY],
|
||||
});
|
||||
return apiKey ? { token: apiKey } : null;
|
||||
},
|
||||
fetchUsageSnapshot: async () => ({
|
||||
provider: "xiaomi",
|
||||
displayName: PROVIDER_LABELS.xiaomi,
|
||||
windows: [],
|
||||
}),
|
||||
},
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,186 @@
|
|||
import { describe, expect, it } from "vitest";
|
||||
import { capturePluginRegistration } from "../plugins/captured-registration.js";
|
||||
import type { ProviderCatalogContext } from "../plugins/types.js";
|
||||
import { defineSingleProviderPluginEntry } from "./provider-entry.js";
|
||||
|
||||
function createCatalogContext(
|
||||
config: ProviderCatalogContext["config"] = {},
|
||||
): ProviderCatalogContext {
|
||||
return {
|
||||
config,
|
||||
env: {},
|
||||
resolveProviderApiKey: () => ({ apiKey: "test-key" }),
|
||||
resolveProviderAuth: () => ({
|
||||
apiKey: "test-key",
|
||||
mode: "api_key",
|
||||
source: "env",
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
describe("defineSingleProviderPluginEntry", () => {
|
||||
it("registers a single provider with default wizard metadata", async () => {
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "demo",
|
||||
name: "Demo Provider",
|
||||
description: "Demo provider plugin",
|
||||
provider: {
|
||||
label: "Demo",
|
||||
docsPath: "/providers/demo",
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Demo API key",
|
||||
hint: "Shared key",
|
||||
optionKey: "demoApiKey",
|
||||
flagName: "--demo-api-key",
|
||||
envVar: "DEMO_API_KEY",
|
||||
promptMessage: "Enter Demo API key",
|
||||
defaultModel: "demo/default",
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: () => ({
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://api.demo.test/v1",
|
||||
models: [{ id: "default", name: "Default" }],
|
||||
}),
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const captured = capturePluginRegistration(entry);
|
||||
expect(captured.providers).toHaveLength(1);
|
||||
const provider = captured.providers[0];
|
||||
expect(provider).toMatchObject({
|
||||
id: "demo",
|
||||
label: "Demo",
|
||||
docsPath: "/providers/demo",
|
||||
envVars: ["DEMO_API_KEY"],
|
||||
});
|
||||
expect(provider?.auth).toHaveLength(1);
|
||||
expect(provider?.auth[0]).toMatchObject({
|
||||
id: "api-key",
|
||||
label: "Demo API key",
|
||||
hint: "Shared key",
|
||||
});
|
||||
expect(provider?.auth[0]?.wizard).toMatchObject({
|
||||
choiceId: "demo-api-key",
|
||||
choiceLabel: "Demo API key",
|
||||
groupId: "demo",
|
||||
groupLabel: "Demo",
|
||||
groupHint: "Shared key",
|
||||
methodId: "api-key",
|
||||
});
|
||||
|
||||
const catalog = await provider?.catalog?.run(createCatalogContext());
|
||||
expect(catalog).toEqual({
|
||||
provider: {
|
||||
api: "openai-completions",
|
||||
apiKey: "test-key",
|
||||
baseUrl: "https://api.demo.test/v1",
|
||||
models: [{ id: "default", name: "Default" }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("supports provider overrides, explicit env vars, and extra registration", async () => {
|
||||
const entry = defineSingleProviderPluginEntry({
|
||||
id: "gateway-plugin",
|
||||
name: "Gateway Provider",
|
||||
description: "Gateway provider plugin",
|
||||
provider: {
|
||||
id: "gateway",
|
||||
label: "Gateway",
|
||||
aliases: ["gw"],
|
||||
docsPath: "/providers/gateway",
|
||||
envVars: ["GATEWAY_KEY", "SECONDARY_KEY"],
|
||||
auth: [
|
||||
{
|
||||
methodId: "api-key",
|
||||
label: "Gateway key",
|
||||
hint: "Primary key",
|
||||
optionKey: "gatewayKey",
|
||||
flagName: "--gateway-key",
|
||||
envVar: "GATEWAY_KEY",
|
||||
promptMessage: "Enter Gateway key",
|
||||
wizard: {
|
||||
groupId: "shared-gateway",
|
||||
groupLabel: "Shared Gateway",
|
||||
},
|
||||
},
|
||||
],
|
||||
catalog: {
|
||||
buildProvider: () => ({
|
||||
api: "openai-completions",
|
||||
baseUrl: "https://gateway.test/v1",
|
||||
models: [{ id: "router", name: "Router" }],
|
||||
}),
|
||||
allowExplicitBaseUrl: true,
|
||||
},
|
||||
capabilities: {
|
||||
transcriptToolCallIdMode: "strict9",
|
||||
},
|
||||
},
|
||||
register(api) {
|
||||
api.registerWebSearchProvider({
|
||||
id: "gateway-search",
|
||||
label: "Gateway Search",
|
||||
hint: "search",
|
||||
envVars: [],
|
||||
placeholder: "",
|
||||
signupUrl: "https://example.com",
|
||||
credentialPath: "tools.web.search.gateway.apiKey",
|
||||
getCredentialValue: () => undefined,
|
||||
setCredentialValue() {},
|
||||
createTool: () => ({
|
||||
description: "search",
|
||||
parameters: {},
|
||||
execute: async () => ({}),
|
||||
}),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const captured = capturePluginRegistration(entry);
|
||||
expect(captured.providers).toHaveLength(1);
|
||||
expect(captured.webSearchProviders).toHaveLength(1);
|
||||
|
||||
const provider = captured.providers[0];
|
||||
expect(provider).toMatchObject({
|
||||
id: "gateway",
|
||||
label: "Gateway",
|
||||
aliases: ["gw"],
|
||||
envVars: ["GATEWAY_KEY", "SECONDARY_KEY"],
|
||||
capabilities: {
|
||||
transcriptToolCallIdMode: "strict9",
|
||||
},
|
||||
});
|
||||
expect(provider?.auth[0]?.wizard).toMatchObject({
|
||||
choiceId: "gateway-api-key",
|
||||
groupId: "shared-gateway",
|
||||
groupLabel: "Shared Gateway",
|
||||
groupHint: "Primary key",
|
||||
});
|
||||
|
||||
const catalog = await provider?.catalog?.run(
|
||||
createCatalogContext({
|
||||
models: {
|
||||
providers: {
|
||||
gateway: {
|
||||
baseUrl: "https://override.test/v1",
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
);
|
||||
expect(catalog).toEqual({
|
||||
provider: {
|
||||
api: "openai-completions",
|
||||
apiKey: "test-key",
|
||||
baseUrl: "https://override.test/v1",
|
||||
models: [{ id: "router", name: "Router" }],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
import type { ProviderPlugin, ProviderPluginWizardSetup } from "../plugins/types.js";
|
||||
import { definePluginEntry } from "./plugin-entry.js";
|
||||
import type {
|
||||
OpenClawPluginApi,
|
||||
OpenClawPluginConfigSchema,
|
||||
OpenClawPluginDefinition,
|
||||
} from "./plugin-entry.js";
|
||||
import { createProviderApiKeyAuthMethod } from "./provider-auth.js";
|
||||
import { buildSingleProviderApiKeyCatalog } from "./provider-catalog.js";
|
||||
|
||||
type ApiKeyAuthMethodOptions = Parameters<typeof createProviderApiKeyAuthMethod>[0];
|
||||
|
||||
export type SingleProviderPluginApiKeyAuthOptions = Omit<
|
||||
ApiKeyAuthMethodOptions,
|
||||
"providerId" | "expectedProviders" | "wizard"
|
||||
> & {
|
||||
expectedProviders?: string[];
|
||||
wizard?: false | ProviderPluginWizardSetup;
|
||||
};
|
||||
|
||||
export type SingleProviderPluginCatalogOptions = {
|
||||
buildProvider: Parameters<typeof buildSingleProviderApiKeyCatalog>[0]["buildProvider"];
|
||||
allowExplicitBaseUrl?: boolean;
|
||||
};
|
||||
|
||||
export type SingleProviderPluginOptions = {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
kind?: OpenClawPluginDefinition["kind"];
|
||||
configSchema?: OpenClawPluginConfigSchema | (() => OpenClawPluginConfigSchema);
|
||||
provider?: {
|
||||
id?: string;
|
||||
label: string;
|
||||
docsPath: string;
|
||||
aliases?: string[];
|
||||
envVars?: string[];
|
||||
auth?: SingleProviderPluginApiKeyAuthOptions[];
|
||||
catalog: SingleProviderPluginCatalogOptions;
|
||||
} & Omit<
|
||||
ProviderPlugin,
|
||||
"id" | "label" | "docsPath" | "aliases" | "envVars" | "auth" | "catalog"
|
||||
>;
|
||||
register?: (api: OpenClawPluginApi) => void;
|
||||
};
|
||||
|
||||
function resolveWizardSetup(params: {
|
||||
providerId: string;
|
||||
providerLabel: string;
|
||||
auth: SingleProviderPluginApiKeyAuthOptions;
|
||||
}): ProviderPluginWizardSetup | undefined {
|
||||
if (params.auth.wizard === false) {
|
||||
return undefined;
|
||||
}
|
||||
const wizard = params.auth.wizard ?? {};
|
||||
const methodId = params.auth.methodId.trim();
|
||||
return {
|
||||
choiceId: wizard.choiceId ?? `${params.providerId}-${methodId}`,
|
||||
choiceLabel: wizard.choiceLabel ?? params.auth.label,
|
||||
...(wizard.choiceHint ? { choiceHint: wizard.choiceHint } : {}),
|
||||
groupId: wizard.groupId ?? params.providerId,
|
||||
groupLabel: wizard.groupLabel ?? params.providerLabel,
|
||||
...((wizard.groupHint ?? params.auth.hint)
|
||||
? { groupHint: wizard.groupHint ?? params.auth.hint }
|
||||
: {}),
|
||||
methodId,
|
||||
...(wizard.onboardingScopes ? { onboardingScopes: wizard.onboardingScopes } : {}),
|
||||
...(wizard.modelAllowlist ? { modelAllowlist: wizard.modelAllowlist } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function resolveEnvVars(params: {
|
||||
envVars?: string[];
|
||||
auth?: SingleProviderPluginApiKeyAuthOptions[];
|
||||
}): string[] | undefined {
|
||||
const combined = [
|
||||
...(params.envVars ?? []),
|
||||
...(params.auth ?? []).map((entry) => entry.envVar).filter(Boolean),
|
||||
]
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
return combined.length > 0 ? [...new Set(combined)] : undefined;
|
||||
}
|
||||
|
||||
export function defineSingleProviderPluginEntry(options: SingleProviderPluginOptions) {
|
||||
return definePluginEntry({
|
||||
id: options.id,
|
||||
name: options.name,
|
||||
description: options.description,
|
||||
...(options.kind ? { kind: options.kind } : {}),
|
||||
...(options.configSchema ? { configSchema: options.configSchema } : {}),
|
||||
register(api) {
|
||||
const provider = options.provider;
|
||||
if (provider) {
|
||||
const providerId = provider.id ?? options.id;
|
||||
const envVars = resolveEnvVars({
|
||||
envVars: provider.envVars,
|
||||
auth: provider.auth,
|
||||
});
|
||||
const auth = (provider.auth ?? []).map((entry) => {
|
||||
const { wizard: _wizard, ...authParams } = entry;
|
||||
const wizard = resolveWizardSetup({
|
||||
providerId,
|
||||
providerLabel: provider.label,
|
||||
auth: entry,
|
||||
});
|
||||
return createProviderApiKeyAuthMethod({
|
||||
...authParams,
|
||||
providerId,
|
||||
expectedProviders: entry.expectedProviders ?? [providerId],
|
||||
...(wizard ? { wizard } : {}),
|
||||
});
|
||||
});
|
||||
api.registerProvider({
|
||||
id: providerId,
|
||||
label: provider.label,
|
||||
docsPath: provider.docsPath,
|
||||
...(provider.aliases ? { aliases: provider.aliases } : {}),
|
||||
...(envVars ? { envVars } : {}),
|
||||
auth,
|
||||
catalog: {
|
||||
order: "simple",
|
||||
run: (ctx) =>
|
||||
buildSingleProviderApiKeyCatalog({
|
||||
ctx,
|
||||
providerId,
|
||||
buildProvider: provider.catalog.buildProvider,
|
||||
...(provider.catalog.allowExplicitBaseUrl ? { allowExplicitBaseUrl: true } : {}),
|
||||
}),
|
||||
},
|
||||
...Object.fromEntries(
|
||||
Object.entries(provider).filter(
|
||||
([key]) =>
|
||||
!["id", "label", "docsPath", "aliases", "envVars", "auth", "catalog"].includes(key),
|
||||
),
|
||||
),
|
||||
});
|
||||
}
|
||||
options.register?.(api);
|
||||
},
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue