@kody-ade/kody-engine 0.4.596 → 0.4.597

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.
Files changed (2) hide show
  1. package/dist/bin/kody.js +97 -22
  2. package/package.json +1 -1
package/dist/bin/kody.js CHANGED
@@ -15,7 +15,7 @@ var init_package = __esm({
15
15
  "package.json"() {
16
16
  package_default = {
17
17
  name: "@kody-ade/kody-engine",
18
- version: "0.4.596",
18
+ version: "0.4.597",
19
19
  description: "kody \u2014 autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
20
20
  license: "MIT",
21
21
  type: "module",
@@ -4228,7 +4228,7 @@ async function runAgent(opts) {
4228
4228
  }
4229
4229
  try {
4230
4230
  const queryOptions = {
4231
- model: opts.litellmUrl ? litellmModelGroup(opts.model) : opts.model.model,
4231
+ model: opts.litellmUrl ? opts.litellmModelGroupOverride ?? litellmModelGroup(opts.model) : opts.model.model,
4232
4232
  cwd: opts.cwd,
4233
4233
  // Fresh array (never mutate the shared DEFAULT_ALLOWED_TOOLS const) so
4234
4234
  // opt-in tools like fetch_repo can be appended below.
@@ -7850,6 +7850,43 @@ function generateLitellmConfigYaml(model) {
7850
7850
  ]);
7851
7851
  return ["model_list:", ...modelEntries, "", "litellm_settings:", " drop_params: true", ""].join("\n");
7852
7852
  }
7853
+ function modelEntry(modelName, model) {
7854
+ const apiKeyVar = model.apiKeyEnvVar ?? providerApiKeyEnvVar(model.provider);
7855
+ const litellmProvider = model.litellmProvider ?? model.provider;
7856
+ return [
7857
+ ` - model_name: ${modelName}`,
7858
+ ` litellm_params:`,
7859
+ ` model: ${litellmProvider}/${model.model}`,
7860
+ ` api_key: os.environ/${apiKeyVar}`,
7861
+ ...model.baseURL ? [` api_base: ${model.baseURL}`] : []
7862
+ ];
7863
+ }
7864
+ function generateAutomaticLitellmConfigYaml(models) {
7865
+ if (models.length < 2) throw new Error("Automatic LiteLLM requires at least two models");
7866
+ const groups = models.map((_, index) => `kody-automatic-${index}`);
7867
+ const entries = models.flatMap((model, index) => modelEntry(groups[index], model));
7868
+ for (const alias of CLAUDE_CODE_PROXY_MODEL_ALIASES) entries.push(...modelEntry(alias, models[0]));
7869
+ const fallbackSources = [groups[0], ...CLAUDE_CODE_PROXY_MODEL_ALIASES, ...groups.slice(1, -1)];
7870
+ const fallbacks = fallbackSources.map((source) => {
7871
+ const sourceIndex = source.startsWith("kody-automatic-") ? Number(source.slice("kody-automatic-".length)) : 0;
7872
+ return ` - ${source}: [${groups.slice(sourceIndex + 1).join(", ")}]`;
7873
+ });
7874
+ return [
7875
+ "model_list:",
7876
+ ...entries,
7877
+ "",
7878
+ "litellm_settings:",
7879
+ " drop_params: true",
7880
+ "",
7881
+ "router_settings:",
7882
+ " num_retries: 0",
7883
+ " allowed_fails: 0",
7884
+ " disable_cooldowns: true",
7885
+ " fallbacks:",
7886
+ ...fallbacks,
7887
+ ""
7888
+ ].join("\n");
7889
+ }
7853
7890
  function litellmModelGroups(model) {
7854
7891
  const primary = litellmModelGroup(model);
7855
7892
  return Array.from(/* @__PURE__ */ new Set([primary, ...CLAUDE_CODE_PROXY_MODEL_ALIASES]));
@@ -7922,9 +7959,29 @@ function resolveLitellmCommand() {
7922
7959
  }
7923
7960
  async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL, runtimeEnvironment = {}) {
7924
7961
  if (!needsLitellmProxy(model)) return null;
7962
+ return startLitellmProxy({
7963
+ projectDir,
7964
+ url,
7965
+ runtimeEnvironment,
7966
+ configYaml: generateLitellmConfigYaml(model),
7967
+ requiredModelGroups: litellmModelGroups(model)
7968
+ });
7969
+ }
7970
+ async function startAutomaticLitellm(models, projectDir, runtimeEnvironment = {}, url = LITELLM_DEFAULT_URL) {
7971
+ const handle = await startLitellmProxy({
7972
+ projectDir,
7973
+ url,
7974
+ runtimeEnvironment,
7975
+ configYaml: generateAutomaticLitellmConfigYaml(models),
7976
+ requiredModelGroups: [AUTOMATIC_LITELLM_MODEL_GROUP, ...CLAUDE_CODE_PROXY_MODEL_ALIASES]
7977
+ });
7978
+ return { ...handle, modelGroup: AUTOMATIC_LITELLM_MODEL_GROUP };
7979
+ }
7980
+ async function startLitellmProxy(input) {
7981
+ const { projectDir, runtimeEnvironment } = input;
7925
7982
  const cmd = resolveLitellmCommand();
7926
- let activeUrl = url.replace(/\/+$/, "");
7927
- const modelGroups = litellmModelGroups(model);
7983
+ let activeUrl = input.url.replace(/\/+$/, "");
7984
+ const modelGroups = input.requiredModelGroups;
7928
7985
  const childEnv = stripBlockingEnv({
7929
7986
  ...process.env,
7930
7987
  ...readDotenvApiKeys(projectDir),
@@ -7936,7 +7993,7 @@ async function startLitellmIfNeeded(model, projectDir, url = LITELLM_DEFAULT_URL
7936
7993
  const portMatch = activeUrl.match(/:(\d+)/);
7937
7994
  const port = portMatch ? portMatch[1] : "4000";
7938
7995
  const configPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.yaml`);
7939
- fs28.writeFileSync(configPath, generateLitellmConfigYaml(model));
7996
+ fs28.writeFileSync(configPath, input.configYaml);
7940
7997
  const args = ["--config", configPath, "--port", port];
7941
7998
  const nextLogPath = path27.join(os4.tmpdir(), `kody-local-litellm-${Date.now()}.log`);
7942
7999
  const outFd = fs28.openSync(nextLogPath, "w");
@@ -8062,7 +8119,7 @@ function stripBlockingEnv(env) {
8062
8119
  delete out.AI_BASE_URL;
8063
8120
  return out;
8064
8121
  }
8065
- var LITELLM_PIP_PACKAGES, DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC, LITELLM_HEALTH_POLL_INTERVAL_MS, CLAUDE_CODE_PROXY_MODEL_ALIASES;
8122
+ var LITELLM_PIP_PACKAGES, DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC, LITELLM_HEALTH_POLL_INTERVAL_MS, CLAUDE_CODE_PROXY_MODEL_ALIASES, AUTOMATIC_LITELLM_MODEL_GROUP;
8066
8123
  var init_litellm = __esm({
8067
8124
  "src/litellm.ts"() {
8068
8125
  "use strict";
@@ -8071,6 +8128,7 @@ var init_litellm = __esm({
8071
8128
  DEFAULT_LITELLM_STARTUP_TIMEOUT_SEC = 150;
8072
8129
  LITELLM_HEALTH_POLL_INTERVAL_MS = 2e3;
8073
8130
  CLAUDE_CODE_PROXY_MODEL_ALIASES = ["claude-haiku-4-5-20251001", "haiku"];
8131
+ AUTOMATIC_LITELLM_MODEL_GROUP = "kody-automatic-0";
8074
8132
  }
8075
8133
  });
8076
8134
 
@@ -22454,6 +22512,19 @@ async function runImplementation(profileName, input) {
22454
22512
  const syntheticPath = ctx.data.syntheticPluginPath;
22455
22513
  const pluginPaths = [...externalPlugins, ...syntheticPath ? [syntheticPath] : []];
22456
22514
  const agents = loadSubagents(profile);
22515
+ if (modelSpec === "automatic") {
22516
+ const automaticEnvironment = {};
22517
+ for (const candidate of modelCandidates) {
22518
+ const resolved2 = await resolveRuntimeModelEnvironment(candidate, ctx);
22519
+ Object.assign(automaticEnvironment, resolved2.environment);
22520
+ for (const warning of resolved2.warnings) process.stderr.write(`\u26A0 ${warning}
22521
+ `);
22522
+ }
22523
+ const lm = await startAutomaticLitellm(modelCandidates, input.cwd, automaticEnvironment);
22524
+ activeLiteLLM.add(lm);
22525
+ const result = await invokeModel(modelCandidates[0], lm, automaticEnvironment, lm.modelGroup);
22526
+ return result;
22527
+ }
22457
22528
  let finalResult;
22458
22529
  for (let candidateIndex = 0; candidateIndex < modelCandidates.length; candidateIndex++) {
22459
22530
  const candidate = modelCandidates[candidateIndex];
@@ -22473,7 +22544,24 @@ async function runImplementation(profileName, input) {
22473
22544
  }
22474
22545
  ctx.data.jobModelProvider = candidate.provider;
22475
22546
  ctx.data.jobModelName = candidate.model;
22476
- const result = await runAgent({
22547
+ const result = await invokeModel(candidate, lm, runtimeModelEnvironment);
22548
+ finalResult = result;
22549
+ const next = modelCandidates[candidateIndex + 1];
22550
+ if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
22551
+ return result;
22552
+ }
22553
+ process.stderr.write(
22554
+ `[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
22555
+ `
22556
+ );
22557
+ if (lm) {
22558
+ lm.kill();
22559
+ activeLiteLLM.delete(lm);
22560
+ }
22561
+ }
22562
+ return finalResult;
22563
+ async function invokeModel(candidate, lm, runtimeModelEnvironment, litellmModelGroupOverride) {
22564
+ return runAgent({
22477
22565
  prompt,
22478
22566
  model: candidate,
22479
22567
  cwd: input.cwd,
@@ -22482,6 +22570,7 @@ async function runImplementation(profileName, input) {
22482
22570
  ...runtimeModelEnvironment
22483
22571
  },
22484
22572
  litellmUrl: lm?.url ?? null,
22573
+ litellmModelGroupOverride,
22485
22574
  // On a connection drop mid-run, restart the (possibly crashed) proxy
22486
22575
  // before the agent retries. No-op for direct-Anthropic runs (lm null).
22487
22576
  ensureBackend: lm ? () => lm.ensureHealthy().then(() => void 0) : void 0,
@@ -22491,7 +22580,7 @@ async function runImplementation(profileName, input) {
22491
22580
  verbose: input.verbose,
22492
22581
  quiet: input.quiet,
22493
22582
  abortController: input.abortController,
22494
- stopOnRateLimit: candidateIndex < modelCandidates.length - 1,
22583
+ stopOnRateLimit: false,
22495
22584
  deadlineAtMs: input.deadlineAtMs,
22496
22585
  ndjsonDir,
22497
22586
  additionalDirectories: agentTaskArtifacts ? [agentTaskArtifacts.absDir] : void 0,
@@ -22541,21 +22630,7 @@ async function runImplementation(profileName, input) {
22541
22630
  schema: ctx.data.capabilityOutputSchema
22542
22631
  } : void 0
22543
22632
  });
22544
- finalResult = result;
22545
- const next = modelCandidates[candidateIndex + 1];
22546
- if (!next || result.outcome !== "failed" || result.outcomeKind !== "rate_limit" || result.safeToReplay !== true) {
22547
- return result;
22548
- }
22549
- process.stderr.write(
22550
- `[kody agent] ${candidate.spec ?? candidate.model} is rate limited; continuing with ${next.spec ?? next.model}
22551
- `
22552
- );
22553
- if (lm) {
22554
- lm.kill();
22555
- activeLiteLLM.delete(lm);
22556
- }
22557
22633
  }
22558
- return finalResult;
22559
22634
  };
22560
22635
  ctx.data.__invokeAgent = invokeAgent;
22561
22636
  const cc = profile.claudeCode;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kody-ade/kody-engine",
3
- "version": "0.4.596",
3
+ "version": "0.4.597",
4
4
  "description": "kody — autonomous development engine. Single-session Claude Code agent behind a generic executor + declarative implementation profiles.",
5
5
  "license": "MIT",
6
6
  "type": "module",