mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 12:37:40 +00:00
Feat/litellm provider (#12823)
* feat: add LiteLLM provider types, env var, credentials, and auth choice Add litellm-api-key auth choice, LITELLM_API_KEY env var mapping, setLitellmApiKey() credential storage, and LITELLM_DEFAULT_MODEL_REF. * feat: add LiteLLM onboarding handler and provider config Add applyLitellmProviderConfig which properly registers models.providers.litellm with baseUrl, api type, and model definitions. This fixes the critical bug from PR #6488 where the provider entry was never created, causing model resolution to fail at runtime. * docs: add LiteLLM provider documentation Add setup guide covering onboarding, manual config, virtual keys, model routing, and usage tracking. Link from provider index. * docs: add LiteLLM to sidebar navigation in docs.json Add providers/litellm to both English and Chinese provider page lists so the docs page appears in the sidebar navigation. * test: add LiteLLM non-interactive onboarding test Wire up litellmApiKey flag inference and auth-choice handler for the non-interactive onboarding path, and add an integration test covering profile, model default, and credential storage. * fix: register --litellm-api-key CLI flag and add preferred provider mapping Wire up the missing Commander CLI option, action handler mapping, and help text for --litellm-api-key. Add litellm-api-key to the preferred provider map for consistency with other providers. * fix: remove zh-CN sidebar entry for litellm (no localized page yet) * style: format buildLitellmModelDefinition return type * fix(onboarding): harden LiteLLM provider setup (#12823) * refactor(onboarding): keep auth-choice provider dispatcher under size limit --------- Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
@@ -19,6 +19,8 @@ import {
|
||||
applyQianfanProviderConfig,
|
||||
applyKimiCodeConfig,
|
||||
applyKimiCodeProviderConfig,
|
||||
applyLitellmConfig,
|
||||
applyLitellmProviderConfig,
|
||||
applyMoonshotConfig,
|
||||
applyMoonshotConfigCn,
|
||||
applyMoonshotProviderConfig,
|
||||
@@ -39,6 +41,7 @@ import {
|
||||
applyXiaomiProviderConfig,
|
||||
applyZaiConfig,
|
||||
CLOUDFLARE_AI_GATEWAY_DEFAULT_MODEL_REF,
|
||||
LITELLM_DEFAULT_MODEL_REF,
|
||||
QIANFAN_DEFAULT_MODEL_REF,
|
||||
KIMI_CODING_MODEL_REF,
|
||||
MOONSHOT_DEFAULT_MODEL_REF,
|
||||
@@ -51,6 +54,7 @@ import {
|
||||
setCloudflareAiGatewayConfig,
|
||||
setQianfanApiKey,
|
||||
setGeminiApiKey,
|
||||
setLitellmApiKey,
|
||||
setKimiCodingApiKey,
|
||||
setMoonshotApiKey,
|
||||
setOpencodeZenApiKey,
|
||||
@@ -89,6 +93,8 @@ export async function applyAuthChoiceApiProviders(
|
||||
) {
|
||||
if (params.opts.tokenProvider === "openrouter") {
|
||||
authChoice = "openrouter-api-key";
|
||||
} else if (params.opts.tokenProvider === "litellm") {
|
||||
authChoice = "litellm-api-key";
|
||||
} else if (params.opts.tokenProvider === "vercel-ai-gateway") {
|
||||
authChoice = "ai-gateway-api-key";
|
||||
} else if (params.opts.tokenProvider === "cloudflare-ai-gateway") {
|
||||
@@ -197,6 +203,69 @@ export async function applyAuthChoiceApiProviders(
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "litellm-api-key") {
|
||||
const store = ensureAuthProfileStore(params.agentDir, { allowKeychainPrompt: false });
|
||||
const profileOrder = resolveAuthProfileOrder({ cfg: nextConfig, store, provider: "litellm" });
|
||||
const existingProfileId = profileOrder.find((profileId) => Boolean(store.profiles[profileId]));
|
||||
const existingCred = existingProfileId ? store.profiles[existingProfileId] : undefined;
|
||||
let profileId = "litellm:default";
|
||||
let hasCredential = false;
|
||||
|
||||
if (existingProfileId && existingCred?.type === "api_key") {
|
||||
profileId = existingProfileId;
|
||||
hasCredential = true;
|
||||
}
|
||||
if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "litellm") {
|
||||
await setLitellmApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
if (!hasCredential) {
|
||||
await params.prompter.note(
|
||||
"LiteLLM provides a unified API to 100+ LLM providers.\nGet your API key from your LiteLLM proxy or https://litellm.ai\nDefault proxy runs on http://localhost:4000",
|
||||
"LiteLLM",
|
||||
);
|
||||
const envKey = resolveEnvApiKey("litellm");
|
||||
if (envKey) {
|
||||
const useExisting = await params.prompter.confirm({
|
||||
message: `Use existing LITELLM_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`,
|
||||
initialValue: true,
|
||||
});
|
||||
if (useExisting) {
|
||||
await setLitellmApiKey(envKey.apiKey, params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
}
|
||||
if (!hasCredential) {
|
||||
const key = await params.prompter.text({
|
||||
message: "Enter LiteLLM API key",
|
||||
validate: validateApiKeyInput,
|
||||
});
|
||||
await setLitellmApiKey(normalizeApiKeyInput(String(key)), params.agentDir);
|
||||
hasCredential = true;
|
||||
}
|
||||
}
|
||||
if (hasCredential) {
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId,
|
||||
provider: "litellm",
|
||||
mode: "api_key",
|
||||
});
|
||||
}
|
||||
const applied = await applyDefaultModelChoice({
|
||||
config: nextConfig,
|
||||
setDefaultModel: params.setDefaultModel,
|
||||
defaultModel: LITELLM_DEFAULT_MODEL_REF,
|
||||
applyDefaultConfig: applyLitellmConfig,
|
||||
applyProviderConfig: applyLitellmProviderConfig,
|
||||
noteDefault: LITELLM_DEFAULT_MODEL_REF,
|
||||
noteAgentModel,
|
||||
prompter: params.prompter,
|
||||
});
|
||||
nextConfig = applied.config;
|
||||
agentModelOverride = applied.agentModelOverride ?? agentModelOverride;
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
|
||||
if (authChoice === "ai-gateway-api-key") {
|
||||
let hasCredential = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user