@kody-ade/kody-engine-lite 0.1.58 → 0.1.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin/cli.js CHANGED
@@ -252,6 +252,17 @@ var init_logger = __esm({
252
252
  // src/config.ts
253
253
  import * as fs from "fs";
254
254
  import * as path from "path";
255
+ function needsLitellmProxy(config) {
256
+ if (config.agent.litellmUrl) return true;
257
+ if (config.agent.provider && config.agent.provider !== "anthropic") return true;
258
+ return false;
259
+ }
260
+ function getLitellmUrl(config) {
261
+ return config.agent.litellmUrl ?? LITELLM_DEFAULT_URL;
262
+ }
263
+ function providerApiKeyEnvVar(provider) {
264
+ return `${provider.toUpperCase()}_API_KEY`;
265
+ }
255
266
  function setConfigDir(dir) {
256
267
  _configDir = dir;
257
268
  _config = null;
@@ -279,7 +290,7 @@ function getProjectConfig() {
279
290
  }
280
291
  return _config;
281
292
  }
282
- var DEFAULT_CONFIG, VERIFY_COMMAND_TIMEOUT_MS, FIX_COMMAND_TIMEOUT_MS, _config, _configDir;
293
+ var DEFAULT_CONFIG, LITELLM_DEFAULT_PORT, LITELLM_DEFAULT_URL, TIER_TO_ANTHROPIC_IDS, VERIFY_COMMAND_TIMEOUT_MS, FIX_COMMAND_TIMEOUT_MS, _config, _configDir;
283
294
  var init_config = __esm({
284
295
  "src/config.ts"() {
285
296
  "use strict";
@@ -313,6 +324,13 @@ var init_config = __esm({
313
324
  tokenBudget: 8e3
314
325
  }
315
326
  };
327
+ LITELLM_DEFAULT_PORT = 4e3;
328
+ LITELLM_DEFAULT_URL = `http://localhost:${LITELLM_DEFAULT_PORT}`;
329
+ TIER_TO_ANTHROPIC_IDS = {
330
+ cheap: ["claude-haiku-4-5-20251001", "claude-haiku-4-5", "haiku"],
331
+ mid: ["claude-sonnet-4-6-20250514", "claude-sonnet-4-6", "sonnet"],
332
+ strong: ["claude-opus-4-6-20250514", "claude-opus-4-6", "opus"]
333
+ };
316
334
  VERIFY_COMMAND_TIMEOUT_MS = 5 * 60 * 1e3;
317
335
  FIX_COMMAND_TIMEOUT_MS = 2 * 60 * 1e3;
318
336
  _config = null;
@@ -1350,8 +1368,8 @@ async function executeAgentStage(ctx, def) {
1350
1368
  const runnerName = config.agent.stageRunners?.[def.name] ?? config.agent.defaultRunner ?? Object.keys(ctx.runners)[0] ?? "claude";
1351
1369
  logger.info(` runner=${runnerName} model=${model} timeout=${def.timeout / 1e3}s`);
1352
1370
  const extraEnv = {};
1353
- if (config.agent.litellmUrl) {
1354
- extraEnv.ANTHROPIC_BASE_URL = config.agent.litellmUrl;
1371
+ if (needsLitellmProxy(config)) {
1372
+ extraEnv.ANTHROPIC_BASE_URL = getLitellmUrl(config);
1355
1373
  }
1356
1374
  const sessions = ctx.sessions ?? {};
1357
1375
  const sessionInfo = getSessionInfo(def.name, sessions);
@@ -3072,6 +3090,7 @@ var init_args = __esm({
3072
3090
 
3073
3091
  // src/cli/litellm.ts
3074
3092
  import * as fs19 from "fs";
3093
+ import * as os from "os";
3075
3094
  import * as path18 from "path";
3076
3095
  import { execFileSync as execFileSync10 } from "child_process";
3077
3096
  async function checkLitellmHealth(url) {
@@ -3082,10 +3101,31 @@ async function checkLitellmHealth(url) {
3082
3101
  return false;
3083
3102
  }
3084
3103
  }
3085
- async function tryStartLitellm(url, projectDir) {
3086
- const configPath = path18.join(projectDir, "litellm-config.yaml");
3087
- if (!fs19.existsSync(configPath)) {
3088
- logger.warn("litellm-config.yaml not found \u2014 cannot start proxy");
3104
+ function generateLitellmConfig(provider, modelMap) {
3105
+ const apiKeyVar = providerApiKeyEnvVar(provider);
3106
+ const entries = ["model_list:"];
3107
+ for (const [tier, providerModel] of Object.entries(modelMap)) {
3108
+ const anthropicIds = TIER_TO_ANTHROPIC_IDS[tier];
3109
+ if (!anthropicIds) continue;
3110
+ for (const modelName of anthropicIds) {
3111
+ entries.push(` - model_name: ${modelName}`);
3112
+ entries.push(` litellm_params:`);
3113
+ entries.push(` model: ${provider}/${providerModel}`);
3114
+ entries.push(` api_key: os.environ/${apiKeyVar}`);
3115
+ }
3116
+ }
3117
+ return entries.join("\n") + "\n";
3118
+ }
3119
+ async function tryStartLitellm(url, projectDir, generatedConfig) {
3120
+ const manualConfigPath = path18.join(projectDir, "litellm-config.yaml");
3121
+ let configPath;
3122
+ if (fs19.existsSync(manualConfigPath)) {
3123
+ configPath = manualConfigPath;
3124
+ } else if (generatedConfig) {
3125
+ configPath = path18.join(os.tmpdir(), "kody-litellm-config.yaml");
3126
+ fs19.writeFileSync(configPath, generatedConfig);
3127
+ } else {
3128
+ logger.warn("litellm-config.yaml not found and no provider configured \u2014 cannot start proxy");
3089
3129
  return null;
3090
3130
  }
3091
3131
  const portMatch = url.match(/:(\d+)/);
@@ -3155,6 +3195,7 @@ var init_litellm = __esm({
3155
3195
  "src/cli/litellm.ts"() {
3156
3196
  "use strict";
3157
3197
  init_logger();
3198
+ init_config();
3158
3199
  }
3159
3200
  });
3160
3201
 
@@ -3228,6 +3269,38 @@ var init_task_state = __esm({
3228
3269
  var entry_exports = {};
3229
3270
  import * as fs21 from "fs";
3230
3271
  import * as path20 from "path";
3272
+ async function ensureLitellmProxy(config, projectDir) {
3273
+ if (!needsLitellmProxy(config)) return null;
3274
+ const litellmUrl = getLitellmUrl(config);
3275
+ const proxyRunning = await checkLitellmHealth(litellmUrl);
3276
+ let litellmProcess = null;
3277
+ if (!proxyRunning) {
3278
+ if (config.agent.provider && config.agent.provider !== "anthropic") {
3279
+ const keyVar = providerApiKeyEnvVar(config.agent.provider);
3280
+ if (!process.env[keyVar]) {
3281
+ logger.error(`Provider '${config.agent.provider}' requires ${keyVar} environment variable`);
3282
+ process.exit(1);
3283
+ }
3284
+ }
3285
+ let generatedConfig;
3286
+ if (config.agent.provider && config.agent.provider !== "anthropic") {
3287
+ generatedConfig = generateLitellmConfig(config.agent.provider, config.agent.modelMap);
3288
+ }
3289
+ litellmProcess = await tryStartLitellm(litellmUrl, projectDir, generatedConfig);
3290
+ if (!litellmProcess) {
3291
+ logger.error("LiteLLM is configured but could not be started. Install it with: pip install 'litellm[proxy]'");
3292
+ process.exit(1);
3293
+ }
3294
+ } else {
3295
+ logger.info(`LiteLLM proxy already running at ${litellmUrl}`);
3296
+ }
3297
+ process.env.ANTHROPIC_BASE_URL = litellmUrl;
3298
+ logger.info(`ANTHROPIC_BASE_URL set to ${litellmUrl}`);
3299
+ if (!process.env.ANTHROPIC_API_KEY || !process.env.ANTHROPIC_API_KEY.startsWith("sk-ant-")) {
3300
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-litellm-proxy-key-00000000000000000000000000000000000000000000000000000000000000000000";
3301
+ }
3302
+ return litellmProcess;
3303
+ }
3231
3304
  async function main() {
3232
3305
  const input = parseArgs();
3233
3306
  const projectDir = input.cwd ? path20.resolve(input.cwd) : process.cwd();
@@ -3320,21 +3393,7 @@ async function main() {
3320
3393
  }
3321
3394
  }
3322
3395
  const config2 = getProjectConfig();
3323
- let litellmProcess2 = null;
3324
- if (config2.agent.litellmUrl) {
3325
- const proxyRunning = await checkLitellmHealth(config2.agent.litellmUrl);
3326
- if (!proxyRunning) {
3327
- litellmProcess2 = await tryStartLitellm(config2.agent.litellmUrl, projectDir);
3328
- if (!litellmProcess2) {
3329
- logger.error("LiteLLM is configured (litellmUrl) but could not be started. Install it with: pip install 'litellm[proxy]'");
3330
- process.exit(1);
3331
- }
3332
- }
3333
- process.env.ANTHROPIC_BASE_URL = config2.agent.litellmUrl;
3334
- if (!process.env.ANTHROPIC_API_KEY || !process.env.ANTHROPIC_API_KEY.startsWith("sk-ant-")) {
3335
- process.env.ANTHROPIC_API_KEY = "sk-ant-api03-litellm-proxy-key-00000000000000000000000000000000000000000000000000000000000000000000";
3336
- }
3337
- }
3396
+ const litellmProcess2 = await ensureLitellmProxy(config2, projectDir);
3338
3397
  const runners2 = createRunners(config2);
3339
3398
  const defaultRunnerName2 = config2.agent.defaultRunner ?? Object.keys(runners2)[0] ?? "claude";
3340
3399
  const defaultRunner2 = runners2[defaultRunnerName2];
@@ -3424,10 +3483,10 @@ ${input.feedback}` : reviewContext;
3424
3483
  }
3425
3484
  }
3426
3485
  const config = getProjectConfig();
3427
- let litellmProcess = null;
3486
+ let litellmProcess = await ensureLitellmProxy(config, projectDir);
3428
3487
  const cleanupLitellm = () => {
3429
3488
  if (litellmProcess) {
3430
- litellmProcess.kill();
3489
+ litellmProcess.kill?.();
3431
3490
  litellmProcess = null;
3432
3491
  }
3433
3492
  };
@@ -3440,23 +3499,6 @@ ${input.feedback}` : reviewContext;
3440
3499
  cleanupLitellm();
3441
3500
  process.exit(143);
3442
3501
  });
3443
- if (config.agent.litellmUrl) {
3444
- const proxyRunning = await checkLitellmHealth(config.agent.litellmUrl);
3445
- if (!proxyRunning) {
3446
- litellmProcess = await tryStartLitellm(config.agent.litellmUrl, projectDir);
3447
- if (!litellmProcess) {
3448
- logger.error("LiteLLM is configured (litellmUrl) but could not be started. Install it with: pip install 'litellm[proxy]'");
3449
- process.exit(1);
3450
- }
3451
- } else {
3452
- logger.info(`LiteLLM proxy already running at ${config.agent.litellmUrl}`);
3453
- }
3454
- process.env.ANTHROPIC_BASE_URL = config.agent.litellmUrl;
3455
- logger.info(`ANTHROPIC_BASE_URL set to ${config.agent.litellmUrl}`);
3456
- if (!process.env.ANTHROPIC_API_KEY || !process.env.ANTHROPIC_API_KEY.startsWith("sk-ant-")) {
3457
- process.env.ANTHROPIC_API_KEY = "sk-ant-api03-litellm-proxy-key-00000000000000000000000000000000000000000000000000000000000000000000";
3458
- }
3459
- }
3460
3502
  const runners = createRunners(config);
3461
3503
  const defaultRunnerName = config.agent.defaultRunner ?? Object.keys(runners)[0] ?? "claude";
3462
3504
  const defaultRunner = runners[defaultRunnerName];
@@ -3548,6 +3590,7 @@ var init_entry = __esm({
3548
3590
  init_litellm();
3549
3591
  init_task_resolution();
3550
3592
  init_task_state();
3593
+ init_config();
3551
3594
  main().catch(async (err) => {
3552
3595
  const msg = err instanceof Error ? err.message : String(err);
3553
3596
  console.error(msg);
@@ -125,9 +125,14 @@
125
125
  },
126
126
  "additionalProperties": false
127
127
  },
128
+ "provider": {
129
+ "type": "string",
130
+ "description": "LLM provider name. When set (and not 'anthropic'), engine auto-starts LiteLLM proxy and routes model calls to this provider. modelMap values should be the provider's model names.",
131
+ "examples": ["anthropic", "minimax", "openai", "google"]
132
+ },
128
133
  "litellmUrl": {
129
134
  "type": "string",
130
- "description": "LiteLLM proxy URL for using non-Anthropic models (e.g., 'http://localhost:4000'). Sets ANTHROPIC_BASE_URL for Claude Code.",
135
+ "description": "(Deprecated) Use 'provider' instead. LiteLLM proxy URL for manual setup.",
131
136
  "examples": ["http://localhost:4000"]
132
137
  },
133
138
  "usePerStageRouting": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine-lite",
3
- "version": "0.1.58",
3
+ "version": "0.1.59",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",