From 7b66bc656b63a854536a81df6f7a317831bbb28d Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 10 Sep 2026 14:37:42 +0200 Subject: [PATCH 1/2] feat(models): expose auto frontier choices --- .../lib/ai-gateway/auto-routing-models.test.ts | 17 +++++++++++++++++ .../src/lib/ai-gateway/auto-routing-models.ts | 8 ++++++++ 2 files changed, 25 insertions(+) diff --git a/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts b/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts index ee4e152239..cf8611156f 100644 --- a/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts +++ b/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts @@ -65,6 +65,23 @@ describe('addAutoRoutingModels', () => { ]); }); + test('annotates the frontier auto model with its visible targets', async () => { + const frontierModel = makeModel('kilo-auto/frontier'); + const opusModel = makeModel('anthropic/claude-opus-5'); + const sonnetModel = makeModel('anthropic/claude-sonnet-5'); + + const result = await addAutoRoutingModels([frontierModel, sonnetModel, opusModel]); + + expect(result).toEqual([ + { + ...frontierModel, + autoRouting: { models: ['anthropic/claude-opus-5', 'anthropic/claude-sonnet-5'] }, + }, + sonnetModel, + opusModel, + ]); + }); + test('excludes virtual auto ids and dedupes and sorts candidates', async () => { const efficientModel = makeModel('kilo-auto/efficient'); const balancedModel = makeModel('kilo-auto/balanced'); diff --git a/apps/web/src/lib/ai-gateway/auto-routing-models.ts b/apps/web/src/lib/ai-gateway/auto-routing-models.ts index 220f270226..02c8ca88d8 100644 --- a/apps/web/src/lib/ai-gateway/auto-routing-models.ts +++ b/apps/web/src/lib/ai-gateway/auto-routing-models.ts @@ -1,8 +1,10 @@ import type { OpenRouterModelsResponse } from '@/lib/organizations/organization-types'; import { + FRONTIER_MODE_TO_MODEL, KILO_AUTO_BALANCED_MODEL, KILO_AUTO_EFFICIENT_MODEL, KILO_AUTO_FREE_MODEL, + KILO_AUTO_FRONTIER_MODEL, } from '@/lib/ai-gateway/auto-model'; import { getAutoFreeCandidates } from '@/lib/ai-gateway/auto-model/resolution'; import { isVirtualAutoModelId } from '@kilocode/auto-routing-contracts'; @@ -19,6 +21,7 @@ export async function addAutoRoutingModels( ): Promise { const availableModelIds = new Set(models.map(model => model.id)); if ( + !availableModelIds.has(KILO_AUTO_FRONTIER_MODEL.id) && !availableModelIds.has(KILO_AUTO_BALANCED_MODEL.id) && !availableModelIds.has(KILO_AUTO_EFFICIENT_MODEL.id) && !availableModelIds.has(KILO_AUTO_FREE_MODEL.id) @@ -37,9 +40,14 @@ export async function addAutoRoutingModels( .map(candidate => candidate.model), availableModelIds ); + const frontierModelIds = visibleConcreteModelIds( + Object.values(FRONTIER_MODE_TO_MODEL).map(({ model }) => model), + availableModelIds + ); const freeModelIds = visibleConcreteModelIds(autoFreeCandidates ?? [], availableModelIds); const hideAutoFreeModel = autoFreeCandidates !== null && freeModelIds.length === 0; const autoRoutingChoices = new Map([ + [KILO_AUTO_FRONTIER_MODEL.id, frontierModelIds], [KILO_AUTO_BALANCED_MODEL.id, efficientModelIds], [KILO_AUTO_EFFICIENT_MODEL.id, efficientModelIds], [KILO_AUTO_FREE_MODEL.id, freeModelIds], From d68b720b05464e272cb89329ef1549a6725ca62a Mon Sep 17 00:00:00 2001 From: Christiaan Arnoldus Date: Thu, 10 Sep 2026 14:43:13 +0200 Subject: [PATCH 2/2] feat(models): expose auto small choices --- .../src/lib/ai-gateway/auto-model/index.ts | 9 +++++++++ .../lib/ai-gateway/auto-model/resolution.ts | 9 +++------ .../ai-gateway/auto-routing-models.test.ts | 19 +++++++++++++++++++ .../src/lib/ai-gateway/auto-routing-models.ts | 10 +++++++++- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/apps/web/src/lib/ai-gateway/auto-model/index.ts b/apps/web/src/lib/ai-gateway/auto-model/index.ts index 8dcf285240..c18a6dda36 100644 --- a/apps/web/src/lib/ai-gateway/auto-model/index.ts +++ b/apps/web/src/lib/ai-gateway/auto-model/index.ts @@ -10,6 +10,10 @@ import { type Verbosity, } from '@kilocode/db/schema-types'; import { KIMI_CURRENT_MODEL_ID } from '@/lib/ai-gateway/providers/moonshotai'; +import { + gemma_4_26b_a4b_it_free_model, + GEMMA_4_26B_A4B_IT_ID, +} from '@/lib/ai-gateway/providers/google'; export type AutoModelPricing = { prompt: string; @@ -159,6 +163,11 @@ export const KILO_AUTO_SMALL_MODEL: AutoModel = { opencode_settings: undefined, }; +export const AUTO_SMALL_TARGET_MODELS = { + paid: GEMMA_4_26B_A4B_IT_ID, + free: gemma_4_26b_a4b_it_free_model.public_id, +} as const; + export const KILO_AUTO_EFFICIENT_MODEL: AutoModel = { ...KILO_AUTO_BALANCED_MODEL, id: 'kilo-auto/efficient', diff --git a/apps/web/src/lib/ai-gateway/auto-model/resolution.ts b/apps/web/src/lib/ai-gateway/auto-model/resolution.ts index 7ab67f15f5..74d780406c 100644 --- a/apps/web/src/lib/ai-gateway/auto-model/resolution.ts +++ b/apps/web/src/lib/ai-gateway/auto-model/resolution.ts @@ -1,8 +1,4 @@ import type { FeatureValue } from '@/lib/feature-detection'; -import { - gemma_4_26b_a4b_it_free_model, - GEMMA_4_26B_A4B_IT_ID, -} from '@/lib/ai-gateway/providers/google'; import type { GatewayRequest, OpenRouterChatCompletionRequest, @@ -15,6 +11,7 @@ import type { } from '@/lib/organizations/organization-types'; import { isVirtualAutoModelId, type AutoRoutingDecision } from '@kilocode/auto-routing-contracts'; import { + AUTO_SMALL_TARGET_MODELS, KILO_AUTO_FREE_MODEL, KILO_AUTO_SMALL_MODEL, KILO_AUTO_BALANCED_MODEL, @@ -314,8 +311,8 @@ export async function resolveAutoModel( resolved: { model: (await balancePromise) > 0 - ? GEMMA_4_26B_A4B_IT_ID - : gemma_4_26b_a4b_it_free_model.public_id, + ? AUTO_SMALL_TARGET_MODELS.paid + : AUTO_SMALL_TARGET_MODELS.free, }, }; } diff --git a/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts b/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts index cf8611156f..e26b4b22dd 100644 --- a/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts +++ b/apps/web/src/lib/ai-gateway/auto-routing-models.test.ts @@ -82,6 +82,25 @@ describe('addAutoRoutingModels', () => { ]); }); + test('annotates the small auto model with its visible targets', async () => { + const smallModel = makeModel('kilo-auto/small'); + const paidModel = makeModel('google/gemma-4-26b-a4b-it'); + const freeModel = makeModel('google/gemma-4-26b-a4b-it:free'); + + const result = await addAutoRoutingModels([smallModel, paidModel, freeModel]); + + expect(result).toEqual([ + { + ...smallModel, + autoRouting: { + models: ['google/gemma-4-26b-a4b-it', 'google/gemma-4-26b-a4b-it:free'], + }, + }, + paidModel, + freeModel, + ]); + }); + test('excludes virtual auto ids and dedupes and sorts candidates', async () => { const efficientModel = makeModel('kilo-auto/efficient'); const balancedModel = makeModel('kilo-auto/balanced'); diff --git a/apps/web/src/lib/ai-gateway/auto-routing-models.ts b/apps/web/src/lib/ai-gateway/auto-routing-models.ts index 02c8ca88d8..0a6ca3ae32 100644 --- a/apps/web/src/lib/ai-gateway/auto-routing-models.ts +++ b/apps/web/src/lib/ai-gateway/auto-routing-models.ts @@ -1,10 +1,12 @@ import type { OpenRouterModelsResponse } from '@/lib/organizations/organization-types'; import { + AUTO_SMALL_TARGET_MODELS, FRONTIER_MODE_TO_MODEL, KILO_AUTO_BALANCED_MODEL, KILO_AUTO_EFFICIENT_MODEL, KILO_AUTO_FREE_MODEL, KILO_AUTO_FRONTIER_MODEL, + KILO_AUTO_SMALL_MODEL, } from '@/lib/ai-gateway/auto-model'; import { getAutoFreeCandidates } from '@/lib/ai-gateway/auto-model/resolution'; import { isVirtualAutoModelId } from '@kilocode/auto-routing-contracts'; @@ -24,7 +26,8 @@ export async function addAutoRoutingModels( !availableModelIds.has(KILO_AUTO_FRONTIER_MODEL.id) && !availableModelIds.has(KILO_AUTO_BALANCED_MODEL.id) && !availableModelIds.has(KILO_AUTO_EFFICIENT_MODEL.id) && - !availableModelIds.has(KILO_AUTO_FREE_MODEL.id) + !availableModelIds.has(KILO_AUTO_FREE_MODEL.id) && + !availableModelIds.has(KILO_AUTO_SMALL_MODEL.id) ) { return models; } @@ -45,12 +48,17 @@ export async function addAutoRoutingModels( availableModelIds ); const freeModelIds = visibleConcreteModelIds(autoFreeCandidates ?? [], availableModelIds); + const smallModelIds = visibleConcreteModelIds( + Object.values(AUTO_SMALL_TARGET_MODELS), + availableModelIds + ); const hideAutoFreeModel = autoFreeCandidates !== null && freeModelIds.length === 0; const autoRoutingChoices = new Map([ [KILO_AUTO_FRONTIER_MODEL.id, frontierModelIds], [KILO_AUTO_BALANCED_MODEL.id, efficientModelIds], [KILO_AUTO_EFFICIENT_MODEL.id, efficientModelIds], [KILO_AUTO_FREE_MODEL.id, freeModelIds], + [KILO_AUTO_SMALL_MODEL.id, smallModelIds], ]); return models.flatMap(model => {