refactor: share self hosted provider plugin helpers

This commit is contained in:
Peter Steinberger 2026-03-14 00:40:00 +00:00
parent 66aabf5eaa
commit 81ea997d40
4 changed files with 47 additions and 66 deletions

View File

@ -1,13 +1,11 @@
import { import {
buildSglangProvider, buildSglangProvider,
configureOpenAICompatibleSelfHostedProviderNonInteractive, configureOpenAICompatibleSelfHostedProviderNonInteractive,
discoverOpenAICompatibleSelfHostedProvider,
emptyPluginConfigSchema, emptyPluginConfigSchema,
promptAndConfigureOpenAICompatibleSelfHostedProvider, promptAndConfigureOpenAICompatibleSelfHostedProviderAuth,
type OpenClawPluginApi, type OpenClawPluginApi,
type ProviderAuthContext,
type ProviderAuthMethodNonInteractiveContext, type ProviderAuthMethodNonInteractiveContext,
type ProviderAuthResult,
type ProviderDiscoveryContext,
} from "openclaw/plugin-sdk/core"; } from "openclaw/plugin-sdk/core";
const PROVIDER_ID = "sglang"; const PROVIDER_ID = "sglang";
@ -30,8 +28,8 @@ const sglangPlugin = {
label: "SGLang", label: "SGLang",
hint: "Fast self-hosted OpenAI-compatible server", hint: "Fast self-hosted OpenAI-compatible server",
kind: "custom", kind: "custom",
run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => { run: async (ctx) =>
const result = await promptAndConfigureOpenAICompatibleSelfHostedProvider({ promptAndConfigureOpenAICompatibleSelfHostedProviderAuth({
cfg: ctx.config, cfg: ctx.config,
prompter: ctx.prompter, prompter: ctx.prompter,
providerId: PROVIDER_ID, providerId: PROVIDER_ID,
@ -39,18 +37,7 @@ const sglangPlugin = {
defaultBaseUrl: DEFAULT_BASE_URL, defaultBaseUrl: DEFAULT_BASE_URL,
defaultApiKeyEnvVar: "SGLANG_API_KEY", defaultApiKeyEnvVar: "SGLANG_API_KEY",
modelPlaceholder: "Qwen/Qwen3-8B", modelPlaceholder: "Qwen/Qwen3-8B",
}); }),
return {
profiles: [
{
profileId: result.profileId,
credential: result.credential,
},
],
configPatch: result.config,
defaultModel: result.modelRef,
};
},
runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) => runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) =>
configureOpenAICompatibleSelfHostedProviderNonInteractive({ configureOpenAICompatibleSelfHostedProviderNonInteractive({
ctx, ctx,
@ -64,21 +51,12 @@ const sglangPlugin = {
], ],
discovery: { discovery: {
order: "late", order: "late",
run: async (ctx: ProviderDiscoveryContext) => { run: async (ctx) =>
if (ctx.config.models?.providers?.sglang) { discoverOpenAICompatibleSelfHostedProvider({
return null; ctx,
} providerId: PROVIDER_ID,
const { apiKey, discoveryApiKey } = ctx.resolveProviderApiKey(PROVIDER_ID); buildProvider: buildSglangProvider,
if (!apiKey) { }),
return null;
}
return {
provider: {
...(await buildSglangProvider({ apiKey: discoveryApiKey })),
apiKey,
},
};
},
}, },
wizard: { wizard: {
onboarding: { onboarding: {

View File

@ -1,13 +1,11 @@
import { import {
buildVllmProvider, buildVllmProvider,
configureOpenAICompatibleSelfHostedProviderNonInteractive, configureOpenAICompatibleSelfHostedProviderNonInteractive,
discoverOpenAICompatibleSelfHostedProvider,
emptyPluginConfigSchema, emptyPluginConfigSchema,
promptAndConfigureOpenAICompatibleSelfHostedProvider, promptAndConfigureOpenAICompatibleSelfHostedProviderAuth,
type OpenClawPluginApi, type OpenClawPluginApi,
type ProviderAuthContext,
type ProviderAuthMethodNonInteractiveContext, type ProviderAuthMethodNonInteractiveContext,
type ProviderAuthResult,
type ProviderDiscoveryContext,
} from "openclaw/plugin-sdk/core"; } from "openclaw/plugin-sdk/core";
const PROVIDER_ID = "vllm"; const PROVIDER_ID = "vllm";
@ -30,8 +28,8 @@ const vllmPlugin = {
label: "vLLM", label: "vLLM",
hint: "Local/self-hosted OpenAI-compatible server", hint: "Local/self-hosted OpenAI-compatible server",
kind: "custom", kind: "custom",
run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => { run: async (ctx) =>
const result = await promptAndConfigureOpenAICompatibleSelfHostedProvider({ promptAndConfigureOpenAICompatibleSelfHostedProviderAuth({
cfg: ctx.config, cfg: ctx.config,
prompter: ctx.prompter, prompter: ctx.prompter,
providerId: PROVIDER_ID, providerId: PROVIDER_ID,
@ -39,18 +37,7 @@ const vllmPlugin = {
defaultBaseUrl: DEFAULT_BASE_URL, defaultBaseUrl: DEFAULT_BASE_URL,
defaultApiKeyEnvVar: "VLLM_API_KEY", defaultApiKeyEnvVar: "VLLM_API_KEY",
modelPlaceholder: "meta-llama/Meta-Llama-3-8B-Instruct", modelPlaceholder: "meta-llama/Meta-Llama-3-8B-Instruct",
}); }),
return {
profiles: [
{
profileId: result.profileId,
credential: result.credential,
},
],
configPatch: result.config,
defaultModel: result.modelRef,
};
},
runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) => runNonInteractive: async (ctx: ProviderAuthMethodNonInteractiveContext) =>
configureOpenAICompatibleSelfHostedProviderNonInteractive({ configureOpenAICompatibleSelfHostedProviderNonInteractive({
ctx, ctx,
@ -64,21 +51,12 @@ const vllmPlugin = {
], ],
discovery: { discovery: {
order: "late", order: "late",
run: async (ctx: ProviderDiscoveryContext) => { run: async (ctx) =>
if (ctx.config.models?.providers?.vllm) { discoverOpenAICompatibleSelfHostedProvider({
return null; ctx,
} providerId: PROVIDER_ID,
const { apiKey, discoveryApiKey } = ctx.resolveProviderApiKey(PROVIDER_ID); buildProvider: buildVllmProvider,
if (!apiKey) { }),
return null;
}
return {
provider: {
...(await buildVllmProvider({ apiKey: discoveryApiKey })),
apiKey,
},
};
},
}, },
wizard: { wizard: {
onboarding: { onboarding: {

View File

@ -2,6 +2,7 @@ import { upsertAuthProfileWithLock } from "../agents/auth-profiles.js";
import type { ApiKeyCredential, AuthProfileCredential } from "../agents/auth-profiles/types.js"; import type { ApiKeyCredential, AuthProfileCredential } from "../agents/auth-profiles/types.js";
import type { OpenClawConfig } from "../config/config.js"; import type { OpenClawConfig } from "../config/config.js";
import type { import type {
ProviderDiscoveryContext,
ProviderAuthResult, ProviderAuthResult,
ProviderAuthMethodNonInteractiveContext, ProviderAuthMethodNonInteractiveContext,
ProviderNonInteractiveApiKeyResult, ProviderNonInteractiveApiKeyResult,
@ -181,6 +182,28 @@ export async function promptAndConfigureOpenAICompatibleSelfHostedProviderAuth(
return buildSelfHostedProviderAuthResult(result); return buildSelfHostedProviderAuthResult(result);
} }
export async function discoverOpenAICompatibleSelfHostedProvider<
T extends Record<string, unknown>,
>(params: {
ctx: ProviderDiscoveryContext;
providerId: string;
buildProvider: (params: { apiKey?: string }) => Promise<T>;
}): Promise<{ provider: T & { apiKey: string } } | null> {
if (params.ctx.config.models?.providers?.[params.providerId]) {
return null;
}
const { apiKey, discoveryApiKey } = params.ctx.resolveProviderApiKey(params.providerId);
if (!apiKey) {
return null;
}
return {
provider: {
...(await params.buildProvider({ apiKey: discoveryApiKey })),
apiKey,
},
};
}
function buildMissingNonInteractiveModelIdMessage(params: { function buildMissingNonInteractiveModelIdMessage(params: {
authChoice: string; authChoice: string;
providerLabel: string; providerLabel: string;

View File

@ -17,7 +17,9 @@ export { buildOauthProviderAuthResult } from "./provider-auth-result.js";
export { export {
applyProviderDefaultModel, applyProviderDefaultModel,
configureOpenAICompatibleSelfHostedProviderNonInteractive, configureOpenAICompatibleSelfHostedProviderNonInteractive,
discoverOpenAICompatibleSelfHostedProvider,
promptAndConfigureOpenAICompatibleSelfHostedProvider, promptAndConfigureOpenAICompatibleSelfHostedProvider,
promptAndConfigureOpenAICompatibleSelfHostedProviderAuth,
SELF_HOSTED_DEFAULT_CONTEXT_WINDOW, SELF_HOSTED_DEFAULT_CONTEXT_WINDOW,
SELF_HOSTED_DEFAULT_COST, SELF_HOSTED_DEFAULT_COST,
SELF_HOSTED_DEFAULT_MAX_TOKENS, SELF_HOSTED_DEFAULT_MAX_TOKENS,