From f30e9c466f48734cc1910991ee3563623315e96f Mon Sep 17 00:00:00 2001 From: Charles-Henri ROBICHE Date: Tue, 27 Jan 2026 10:08:47 +0100 Subject: [PATCH] fix(litellm): honor CLI flags during onboarding Add support for --litellm-api-key, --litellm-base-url, and --litellm-model CLI flags to enable non-interactive/automation use cases for LiteLLM provider onboarding. Co-Authored-By: Claude (claude-opus-4-5) --- .../auth-choice.apply.api-providers.ts | 66 ++++++++++++------- src/commands/auth-choice.apply.ts | 6 ++ src/commands/onboard-auth.config-core.ts | 8 +-- 3 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index c3d2c7364..581667350 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -588,7 +588,12 @@ export async function applyAuthChoiceApiProviders( let hasCredential = false; let apiKey: string | undefined; - // Check for pre-provided credentials via CLI options + // Check for pre-provided API key via CLI options (--litellm-api-key or --token with --token-provider litellm) + if (!hasCredential && params.opts?.litellmApiKey) { + apiKey = normalizeApiKeyInput(params.opts.litellmApiKey); + await setLitellmApiKey(apiKey, params.agentDir); + hasCredential = true; + } if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "litellm") { apiKey = normalizeApiKeyInput(params.opts.token); await setLitellmApiKey(apiKey, params.agentDir); @@ -610,7 +615,7 @@ export async function applyAuthChoiceApiProviders( // Check for existing env key const envKey = resolveEnvApiKey("litellm"); - if (envKey) { + if (!hasCredential && envKey) { const useExisting = await params.prompter.confirm({ message: `Use existing LITELLM_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, initialValue: true, @@ -631,24 +636,28 @@ export async function applyAuthChoiceApiProviders( await setLitellmApiKey(apiKey, params.agentDir); } - // Prompt for base URL - const defaultBaseUrl = process.env.LITELLM_BASE_URL ?? "http://localhost:4000"; - const baseUrl = await params.prompter.text({ - message: "Enter LiteLLM base URL", - initialValue: defaultBaseUrl, - placeholder: defaultBaseUrl, - validate: (value) => { - if (!value?.trim()) return "Base URL is required"; - try { - new URL(value); - return undefined; - } catch { - return "Invalid URL format"; - } - }, - }); - - const normalizedBaseUrl = String(baseUrl).trim(); + // Check for pre-provided base URL via CLI option (--litellm-base-url) + let normalizedBaseUrl: string; + if (params.opts?.litellmBaseUrl) { + normalizedBaseUrl = params.opts.litellmBaseUrl.trim(); + } else { + const defaultBaseUrl = process.env.LITELLM_BASE_URL ?? "http://localhost:4000"; + const baseUrl = await params.prompter.text({ + message: "Enter LiteLLM base URL", + initialValue: defaultBaseUrl, + placeholder: defaultBaseUrl, + validate: (value) => { + if (!value?.trim()) return "Base URL is required"; + try { + new URL(value); + return undefined; + } catch { + return "Invalid URL format"; + } + }, + }); + normalizedBaseUrl = String(baseUrl).trim(); + } // Try to fetch available models from LiteLLM type LitellmModelInfo = { id: string; maxInputTokens?: number; maxOutputTokens?: number }; @@ -722,7 +731,18 @@ export async function applyAuthChoiceApiProviders( let contextWindow: number | undefined; let maxTokens: number | undefined; - if (availableModels.length > 0) { + // Check for pre-provided model via CLI option (--litellm-model) + if (params.opts?.litellmModel) { + normalizedModelId = params.opts.litellmModel.trim(); + // Try to get context info from model info map + const modelInfo = availableModels.find((m) => m.id === normalizedModelId); + if (modelInfo?.maxInputTokens) { + contextWindow = modelInfo.maxInputTokens; + } + if (modelInfo?.maxOutputTokens) { + maxTokens = modelInfo.maxOutputTokens; + } + } else if (availableModels.length > 0) { // Let user select from available models type SelectOption = { value: string; label: string; hint?: string }; const modelOptions: SelectOption[] = availableModels.map((m) => ({ @@ -765,8 +785,8 @@ export async function applyAuthChoiceApiProviders( normalizedModelId = String(modelId).trim(); } - // If context window wasn't auto-detected, prompt for it - if (!contextWindow) { + // If context window wasn't auto-detected, prompt for it (skip in non-interactive mode) + if (!contextWindow && !params.opts?.nonInteractive) { const contextInput = await params.prompter.text({ message: "Enter context window size (tokens)", initialValue: "128000", diff --git a/src/commands/auth-choice.apply.ts b/src/commands/auth-choice.apply.ts index f139b509f..c00a56c5a 100644 --- a/src/commands/auth-choice.apply.ts +++ b/src/commands/auth-choice.apply.ts @@ -24,6 +24,12 @@ export type ApplyAuthChoiceParams = { opts?: { tokenProvider?: string; token?: string; + // LiteLLM-specific options + litellmApiKey?: string; + litellmBaseUrl?: string; + litellmModel?: string; + // Non-interactive mode flag + nonInteractive?: boolean; }; }; diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index 2b59d7aa1..2cb457ebb 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -417,7 +417,7 @@ export function applyVeniceConfig(cfg: MoltbotConfig): MoltbotConfig { * are user-configurable. */ export function applyLitellmProviderConfig( - cfg: ClawdbotConfig, + cfg: MoltbotConfig, params: { baseUrl: string; modelId: string; @@ -425,7 +425,7 @@ export function applyLitellmProviderConfig( contextWindow?: number; maxTokens?: number; }, -): ClawdbotConfig { +): MoltbotConfig { const modelRef = `litellm/${params.modelId}`; const models = { ...cfg.agents?.defaults?.models }; models[modelRef] = { @@ -485,7 +485,7 @@ export function applyLitellmProviderConfig( * Use this when LiteLLM is the primary provider choice during onboarding. */ export function applyLitellmConfig( - cfg: ClawdbotConfig, + cfg: MoltbotConfig, params: { baseUrl: string; modelId: string; @@ -493,7 +493,7 @@ export function applyLitellmConfig( contextWindow?: number; maxTokens?: number; }, -): ClawdbotConfig { +): MoltbotConfig { const next = applyLitellmProviderConfig(cfg, params); const modelRef = `litellm/${params.modelId}`; const existingModel = next.agents?.defaults?.model;