diff --git a/apps/web/src/app/api/openrouter/[...path]/route.test.ts b/apps/web/src/app/api/openrouter/[...path]/route.test.ts index a9b1235740..70e5a61990 100644 --- a/apps/web/src/app/api/openrouter/[...path]/route.test.ts +++ b/apps/web/src/app/api/openrouter/[...path]/route.test.ts @@ -498,7 +498,7 @@ describe('POST /api/openrouter/v1/chat/completions rules-engine actions', () => mockedClassifyAbuse.mockResolvedValue(classifyResult(null)); mockedRedisGet.mockResolvedValue(null); mockedRedisSet.mockResolvedValue('OK'); - mockedGetOpenRouterModels.mockResolvedValue(new Set(['stepfun/step-3.7-flash:free'])); + mockedGetOpenRouterModels.mockResolvedValue(new Set(['poolside/laguna-s-2.1:free'])); mockedIsValidOpenRouterModelId.mockResolvedValue(true); mockedUpstreamRequest.mockResolvedValue({ type: 'success', @@ -870,12 +870,8 @@ describe('POST /api/openrouter/v1/chat/completions rules-engine actions', () => expect(response.status).toBe(200); expect(mockedGetProvider).toHaveBeenCalledTimes(2); - expect(mockedGetProvider.mock.calls[1]?.[0].requestedModel).toBe( - stepfun_37_flash_free_model.public_id - ); - expect(mockedUpstreamRequest.mock.calls[0]?.[0].body.model).toBe( - stepfun_37_flash_free_model.internal_id - ); + expect(mockedGetProvider.mock.calls[1]?.[0].requestedModel).toBe('poolside/laguna-s-2.1:free'); + expect(mockedUpstreamRequest.mock.calls[0]?.[0].body.model).toBe('poolside/laguna-s-2.1:free'); expect(mockedAccountForMicrodollarUsage.mock.calls[0]?.[1]).toMatchObject({ abuse_delay: 6000, abuse_downgraded_from: 'openai/gpt-4o', diff --git a/apps/web/src/lib/ai-gateway/auto-model/resolution.test.ts b/apps/web/src/lib/ai-gateway/auto-model/resolution.test.ts index aea7b92e86..2045217e77 100644 --- a/apps/web/src/lib/ai-gateway/auto-model/resolution.test.ts +++ b/apps/web/src/lib/ai-gateway/auto-model/resolution.test.ts @@ -1,10 +1,14 @@ -import { describe, expect, it, jest } from '@jest/globals'; +import { beforeEach, describe, expect, it, jest } from '@jest/globals'; +import type * as GatewayModelsCache from '@/lib/ai-gateway/providers/gateway-models-cache'; jest.mock('@/lib/ai-gateway/providers/gateway-models-cache', () => ({ - getOpenRouterModelsFromDatabase: jest.fn(async () => new Set()), + ...jest.requireActual( + '@/lib/ai-gateway/providers/gateway-models-cache' + ), + getOpenRouterModelsFromDatabase: jest.fn(), })); -import { resolveAutoModel } from './resolution'; +import type * as AutoModelResolution from './resolution'; import { FRONTIER_MODE_TO_MODEL, KILO_AUTO_BALANCED_MODEL, @@ -15,6 +19,8 @@ import { import type { AutoRoutingDecision } from '@kilocode/auto-routing-contracts'; import { PRIMARY_DEFAULT_MODEL } from '@/lib/ai-gateway/models'; +const { resolveAutoModel } = jest.requireActual('./resolution'); + const baseParams = { model: KILO_AUTO_EFFICIENT_MODEL.id, modeHeader: null, @@ -27,6 +33,9 @@ const baseParams = { const nullUserPromise = Promise.resolve(null); const zeroBalancePromise = Promise.resolve(0); const primaryDefaultFallback = { model: PRIMARY_DEFAULT_MODEL }; +const { getOpenRouterModelsFromDatabase: mockedGetOpenRouterModels } = jest.requireMock< + jest.Mocked +>('@/lib/ai-gateway/providers/gateway-models-cache'); const sampleDecision: AutoRoutingDecision = { model: 'anthropic/claude-haiku-4', @@ -406,9 +415,13 @@ describe('resolveAutoModel — kilo-auto/efficient branch', () => { }); describe('resolveAutoModel — kilo-auto/free branch', () => { + beforeEach(() => { + mockedGetOpenRouterModels.mockResolvedValue(new Set(['poolside/laguna-s-2.1:free'])); + }); + it('excludes candidates denied by the effective organization policy', async () => { const isAutoFreeCandidateAllowed = jest.fn( - async (modelId: string) => modelId === 'stepfun/step-3.7-flash:free' + async (modelId: string) => modelId === 'poolside/laguna-s-2.1:free' ); const result = await resolveAutoModel( @@ -425,11 +438,11 @@ describe('resolveAutoModel — kilo-auto/free branch', () => { expect(result).toEqual({ kind: 'ok', resolved: { - model: 'stepfun/step-3.7-flash:free', + model: 'poolside/laguna-s-2.1:free', reasoning: { enabled: true, effort: 'high' }, }, }); - expect(isAutoFreeCandidateAllowed).toHaveBeenCalledWith('stepfun/step-3.7-flash:free'); + expect(isAutoFreeCandidateAllowed).toHaveBeenCalledWith('poolside/laguna-s-2.1:free'); }); it('reports no free models when organization policy denies every candidate', async () => { diff --git a/apps/web/src/lib/ai-gateway/models.test.ts b/apps/web/src/lib/ai-gateway/models.test.ts index b5aa10dfed..c5ebcad600 100644 --- a/apps/web/src/lib/ai-gateway/models.test.ts +++ b/apps/web/src/lib/ai-gateway/models.test.ts @@ -231,7 +231,6 @@ describe('isFreeModel', () => { expect( Object.fromEntries(autoFreeModels.map(({ model, reasoning }) => [model, reasoning])) ).toEqual({ - 'stepfun/step-3.7-flash:free': { enabled: true, effort: 'high' }, 'poolside/laguna-s-2.1:free': { enabled: true, effort: 'high' }, 'nvidia/nemotron-3-ultra-550b-a55b:free': { enabled: true, effort: 'high' }, 'dots-studio/dots-3-note-preview:free': { enabled: true, effort: 'high' }, @@ -244,7 +243,6 @@ describe('isFreeModel', () => { autoFreeModels.map(({ model, weight }) => [model, weight]) ); expect(weights).toEqual({ - 'stepfun/step-3.7-flash:free': 1, 'poolside/laguna-s-2.1:free': 1, 'nvidia/nemotron-3-ultra-550b-a55b:free': 1, 'dots-studio/dots-3-note-preview:free': 1, diff --git a/apps/web/src/lib/ai-gateway/models.ts b/apps/web/src/lib/ai-gateway/models.ts index 1857b2774a..e8c18969c2 100644 --- a/apps/web/src/lib/ai-gateway/models.ts +++ b/apps/web/src/lib/ai-gateway/models.ts @@ -39,15 +39,6 @@ export type AutoFreeModel = { }; export const autoFreeModels: ReadonlyArray = [ - ...(stepfun_37_flash_free_model.status === 'public' - ? [ - { - model: stepfun_37_flash_free_model.public_id, - weight: 1, - reasoning: { enabled: true, effort: 'high' }, - } satisfies AutoFreeModel, - ] - : []), { model: 'poolside/laguna-s-2.1:free', weight: 1,