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) <noreply@anthropic.com>
This commit is contained in:
Charles-Henri ROBICHE 2026-01-27 10:08:47 +01:00
parent efd827b526
commit f30e9c466f
No known key found for this signature in database
3 changed files with 53 additions and 27 deletions

View File

@ -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",

View File

@ -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;
};
};

View File

@ -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;