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

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;
@@ -1223,6 +1241,9 @@ function resolveModel(modelTier, stageName) {
1223
1241
  if (config.agent.usePerStageRouting && stageName) {
1224
1242
  return stageName;
1225
1243
  }
1244
+ if (config.agent.provider && config.agent.provider !== "anthropic") {
1245
+ return DEFAULT_MODEL_MAP[modelTier] ?? "sonnet";
1246
+ }
1226
1247
  const mapped = config.agent.modelMap[modelTier];
1227
1248
  if (mapped) return mapped;
1228
1249
  return DEFAULT_MODEL_MAP[modelTier] ?? "sonnet";
@@ -1350,8 +1371,8 @@ async function executeAgentStage(ctx, def) {
1350
1371
  const runnerName = config.agent.stageRunners?.[def.name] ?? config.agent.defaultRunner ?? Object.keys(ctx.runners)[0] ?? "claude";
1351
1372
  logger.info(` runner=${runnerName} model=${model} timeout=${def.timeout / 1e3}s`);
1352
1373
  const extraEnv = {};
1353
- if (config.agent.litellmUrl) {
1354
- extraEnv.ANTHROPIC_BASE_URL = config.agent.litellmUrl;
1374
+ if (needsLitellmProxy(config)) {
1375
+ extraEnv.ANTHROPIC_BASE_URL = getLitellmUrl(config);
1355
1376
  }
1356
1377
  const sessions = ctx.sessions ?? {};
1357
1378
  const sessionInfo = getSessionInfo(def.name, sessions);
@@ -3072,6 +3093,7 @@ var init_args = __esm({
3072
3093
 
3073
3094
  // src/cli/litellm.ts
3074
3095
  import * as fs19 from "fs";
3096
+ import * as os from "os";
3075
3097
  import * as path18 from "path";
3076
3098
  import { execFileSync as execFileSync10 } from "child_process";
3077
3099
  async function checkLitellmHealth(url) {
@@ -3082,10 +3104,31 @@ async function checkLitellmHealth(url) {
3082
3104
  return false;
3083
3105
  }
3084
3106
  }
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");
3107
+ function generateLitellmConfig(provider, modelMap) {
3108
+ const apiKeyVar = providerApiKeyEnvVar(provider);
3109
+ const entries = ["model_list:"];
3110
+ for (const [tier, providerModel] of Object.entries(modelMap)) {
3111
+ const anthropicIds = TIER_TO_ANTHROPIC_IDS[tier];
3112
+ if (!anthropicIds) continue;
3113
+ for (const modelName of anthropicIds) {
3114
+ entries.push(` - model_name: ${modelName}`);
3115
+ entries.push(` litellm_params:`);
3116
+ entries.push(` model: ${provider}/${providerModel}`);
3117
+ entries.push(` api_key: os.environ/${apiKeyVar}`);
3118
+ }
3119
+ }
3120
+ return entries.join("\n") + "\n";
3121
+ }
3122
+ async function tryStartLitellm(url, projectDir, generatedConfig) {
3123
+ const manualConfigPath = path18.join(projectDir, "litellm-config.yaml");
3124
+ let configPath;
3125
+ if (fs19.existsSync(manualConfigPath)) {
3126
+ configPath = manualConfigPath;
3127
+ } else if (generatedConfig) {
3128
+ configPath = path18.join(os.tmpdir(), "kody-litellm-config.yaml");
3129
+ fs19.writeFileSync(configPath, generatedConfig);
3130
+ } else {
3131
+ logger.warn("litellm-config.yaml not found and no provider configured \u2014 cannot start proxy");
3089
3132
  return null;
3090
3133
  }
3091
3134
  const portMatch = url.match(/:(\d+)/);
@@ -3155,6 +3198,7 @@ var init_litellm = __esm({
3155
3198
  "src/cli/litellm.ts"() {
3156
3199
  "use strict";
3157
3200
  init_logger();
3201
+ init_config();
3158
3202
  }
3159
3203
  });
3160
3204
 
@@ -3228,6 +3272,38 @@ var init_task_state = __esm({
3228
3272
  var entry_exports = {};
3229
3273
  import * as fs21 from "fs";
3230
3274
  import * as path20 from "path";
3275
+ async function ensureLitellmProxy(config, projectDir) {
3276
+ if (!needsLitellmProxy(config)) return null;
3277
+ const litellmUrl = getLitellmUrl(config);
3278
+ const proxyRunning = await checkLitellmHealth(litellmUrl);
3279
+ let litellmProcess = null;
3280
+ if (!proxyRunning) {
3281
+ if (config.agent.provider && config.agent.provider !== "anthropic") {
3282
+ const keyVar = providerApiKeyEnvVar(config.agent.provider);
3283
+ if (!process.env[keyVar]) {
3284
+ logger.error(`Provider '${config.agent.provider}' requires ${keyVar} environment variable`);
3285
+ process.exit(1);
3286
+ }
3287
+ }
3288
+ let generatedConfig;
3289
+ if (config.agent.provider && config.agent.provider !== "anthropic") {
3290
+ generatedConfig = generateLitellmConfig(config.agent.provider, config.agent.modelMap);
3291
+ }
3292
+ litellmProcess = await tryStartLitellm(litellmUrl, projectDir, generatedConfig);
3293
+ if (!litellmProcess) {
3294
+ logger.error("LiteLLM is configured but could not be started. Install it with: pip install 'litellm[proxy]'");
3295
+ process.exit(1);
3296
+ }
3297
+ } else {
3298
+ logger.info(`LiteLLM proxy already running at ${litellmUrl}`);
3299
+ }
3300
+ process.env.ANTHROPIC_BASE_URL = litellmUrl;
3301
+ logger.info(`ANTHROPIC_BASE_URL set to ${litellmUrl}`);
3302
+ if (!process.env.ANTHROPIC_API_KEY || !process.env.ANTHROPIC_API_KEY.startsWith("sk-ant-")) {
3303
+ process.env.ANTHROPIC_API_KEY = "sk-ant-api03-litellm-proxy-key-00000000000000000000000000000000000000000000000000000000000000000000";
3304
+ }
3305
+ return litellmProcess;
3306
+ }
3231
3307
  async function main() {
3232
3308
  const input = parseArgs();
3233
3309
  const projectDir = input.cwd ? path20.resolve(input.cwd) : process.cwd();
@@ -3320,21 +3396,7 @@ async function main() {
3320
3396
  }
3321
3397
  }
3322
3398
  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
- }
3399
+ const litellmProcess2 = await ensureLitellmProxy(config2, projectDir);
3338
3400
  const runners2 = createRunners(config2);
3339
3401
  const defaultRunnerName2 = config2.agent.defaultRunner ?? Object.keys(runners2)[0] ?? "claude";
3340
3402
  const defaultRunner2 = runners2[defaultRunnerName2];
@@ -3424,10 +3486,10 @@ ${input.feedback}` : reviewContext;
3424
3486
  }
3425
3487
  }
3426
3488
  const config = getProjectConfig();
3427
- let litellmProcess = null;
3489
+ let litellmProcess = await ensureLitellmProxy(config, projectDir);
3428
3490
  const cleanupLitellm = () => {
3429
3491
  if (litellmProcess) {
3430
- litellmProcess.kill();
3492
+ litellmProcess.kill?.();
3431
3493
  litellmProcess = null;
3432
3494
  }
3433
3495
  };
@@ -3440,23 +3502,6 @@ ${input.feedback}` : reviewContext;
3440
3502
  cleanupLitellm();
3441
3503
  process.exit(143);
3442
3504
  });
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
3505
  const runners = createRunners(config);
3461
3506
  const defaultRunnerName = config.agent.defaultRunner ?? Object.keys(runners)[0] ?? "claude";
3462
3507
  const defaultRunner = runners[defaultRunnerName];
@@ -3548,6 +3593,7 @@ var init_entry = __esm({
3548
3593
  init_litellm();
3549
3594
  init_task_resolution();
3550
3595
  init_task_state();
3596
+ init_config();
3551
3597
  main().catch(async (err) => {
3552
3598
  const msg = err instanceof Error ? err.message : String(err);
3553
3599
  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.60",
4
4
  "description": "Autonomous SDLC pipeline: Kody orchestration + Claude Code + LiteLLM",
5
5
  "license": "MIT",
6
6
  "type": "module",