Skip to content
Merged
37 changes: 36 additions & 1 deletion apps/web/src/lib/ai-gateway/models.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
claude_sonnet_4_6_stealth_model,
claude_opus_4_6_stealth_model,
} from './providers/anthropic.constants';
import { gpt_5_6_sol_discounted_model } from './providers/openai-exclusive';
import { gpt_5_6_sol_discounted_model, gpt_6_astra_flex_model } from './providers/openai-exclusive';
import { gemma_4_26b_a4b_it_free_model } from './providers/google';
import { isUnavailableModel } from './unavailable-models';
import { getRandomNumber } from './getRandomNumber';
Expand Down Expand Up @@ -152,6 +152,41 @@ describe('isFreeModel', () => {
]);
});

test('keeps the GPT-6 Astra OpenAI Flex endpoint disabled', async () => {
expect(findKiloExclusiveModel(gpt_6_astra_flex_model.public_id)).toBeNull();
expect(gpt_6_astra_flex_model).toMatchObject({
status: 'disabled',
internal_id: 'openai/gpt-6-astra',
gateway: 'vercel',
flags: ['reasoning', 'vision', 'flex'],
inference_provider_restriction: ['openai'],
pricing: { fallbackOnly: true },
});
expect(
await hasBestEffortGuessDataCollectionRequirement(gpt_6_astra_flex_model.public_id)
).toBe(false);
expect(gpt_6_astra_flex_model.pricing?.tiers).toEqual([
{
start_context_length: 0,
pricing: {
prompt_per_million: 5,
completion_per_million: 25,
input_cache_read_per_million: 0.5,
input_cache_write_per_million: 6.25,
},
},
{
start_context_length: 272_000,
pricing: {
prompt_per_million: 10,
completion_per_million: 37.5,
input_cache_read_per_million: 1,
input_cache_write_per_million: 12.5,
},
},
]);
});

test('all Kilo exclusive models should have either no pricing or valid ordered pricing tiers', () => {
for (const model of kiloExclusiveModels) {
if (model.pricing) {
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/lib/ai-gateway/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ import { stepfun_37_flash_free_model } from '@/lib/ai-gateway/providers/stepfun'
import { isGrokModel } from '@/lib/ai-gateway/providers/xai';
import { isClaudeModel } from '@/lib/ai-gateway/providers/anthropic.constants';
import { GPT_CURRENT_MODEL_ID, isOpenAiModel } from '@/lib/ai-gateway/providers/openai';
import { gpt_5_6_sol_discounted_model } from '@/lib/ai-gateway/providers/openai-exclusive';
import {
gpt_5_6_sol_discounted_model,
gpt_6_astra_flex_model,
} from '@/lib/ai-gateway/providers/openai-exclusive';
import { GLM_CURRENT_MODEL_ID } from '@/lib/ai-gateway/providers/zai';
import { type ProviderId } from '@/lib/ai-gateway/providers/types';
import type { OpenRouterReasoningConfig } from '@/lib/ai-gateway/providers/openrouter/types';
Expand Down Expand Up @@ -95,6 +98,7 @@ export const preferredModels = [
...(gpt_5_6_sol_discounted_model.status === 'public'
? [gpt_5_6_sol_discounted_model.public_id]
: []),
...(gpt_6_astra_flex_model.status === 'public' ? [gpt_6_astra_flex_model.public_id] : []),
GLM_CURRENT_MODEL_ID,
KIMI_CURRENT_MODEL_ID,
MINIMAX_CURRENT_MODEL_ID,
Expand Down Expand Up @@ -130,6 +134,7 @@ export const kiloExclusiveModels = [
gemma_4_26b_a4b_it_free_model,
qwen36_plus_stealth_model,
gpt_5_6_sol_discounted_model,
gpt_6_astra_flex_model,
claude_opus_4_8_stealth_model,
claude_opus_4_7_stealth_model,
claude_sonnet_4_6_stealth_model,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { describe, expect, it } from '@jest/globals';
import { describe, expect, it, jest } from '@jest/globals';
import { CLAUDE_OPUS_FALLBACK_MODEL_ID } from '@/lib/ai-gateway/providers/anthropic.constants';
import {
applyAnthropicThinkingDefault,
applyGatewayModelsFallback,
applyPreferredProvider,
applyReasoningDetailsTransform,
removeUnsupportedRequestServiceTier,
} from '@/lib/ai-gateway/providers/apply-provider-specific-logic';
import type { GatewayRequest } from '@/lib/ai-gateway/providers/openrouter/types';
import {
Expand All @@ -13,6 +14,11 @@ import {
type ProviderId,
} from '@/lib/ai-gateway/providers/types';
import { PERPLEXITY_KIMI_PUBLIC_ID } from '@/lib/ai-gateway/providers/partner/constants';
import { QWEN37_MAX_MODEL_ID } from '@/lib/ai-gateway/custom-pricing';
import {
gpt_5_6_sol_discounted_model,
gpt_6_astra_flex_model,
} from '@/lib/ai-gateway/providers/openai-exclusive';

function makeRequest(model: string, models?: string[]): GatewayRequest {
return {
Expand Down Expand Up @@ -85,6 +91,55 @@ describe('applyAnthropicThinkingDefault', () => {
);
});

describe('removeUnsupportedRequestServiceTier', () => {
it.each([
{
model: QWEN37_MAX_MODEL_ID,
kiloExclusiveModel: null,
reason: 'non-fallback custom pricing',
},
{
model: gpt_5_6_sol_discounted_model.public_id,
kiloExclusiveModel: gpt_5_6_sol_discounted_model,
reason: 'non-Flex Kilo-exclusive model',
},
])(
'removes and logs the request-level tier for $reason',
({ model, kiloExclusiveModel, reason }) => {
const request = makeRequest(model);
request.body.service_tier = 'priority';
const warn = jest.spyOn(console, 'warn').mockImplementation(() => undefined);

removeUnsupportedRequestServiceTier(model, request, kiloExclusiveModel);

expect(request.body.service_tier).toBeUndefined();
expect(warn).toHaveBeenCalledWith(
'[applyProviderSpecificLogic] Removed unsupported request-level service tier',
{
model,
requestKind: 'chat_completions',
serviceTier: 'priority',
reason,
}
);
warn.mockRestore();
}
);

it.each([
[PERPLEXITY_KIMI_PUBLIC_ID, null],
[gpt_6_astra_flex_model.public_id, gpt_6_astra_flex_model],
['vendor/standard-model', null],
] as const)('preserves the request-level tier for %s', (model, kiloExclusiveModel) => {
const request = makeRequest(model);
request.body.service_tier = 'priority';

removeUnsupportedRequestServiceTier(model, request, kiloExclusiveModel);

expect(request.body.service_tier).toBe('priority');
});
});

describe('applyReasoningDetailsTransform', () => {
function makeProvider(responseTransforms: Provider['responseTransforms']): Provider {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,10 @@ import {
} from '@/lib/ai-gateway/providers/openrouter/types';
import { applyMistralModelSettings, isMistralModel } from '@/lib/ai-gateway/providers/mistral';
import { findKiloExclusiveModel } from '@/lib/ai-gateway/models';
import { applyKiloExclusiveModelSettings } from '@/lib/ai-gateway/providers/kilo-exclusive-model';
import {
applyKiloExclusiveModelSettings,
type KiloExclusiveModel,
} from '@/lib/ai-gateway/providers/kilo-exclusive-model';
import { applyAnthropicModelSettings } from '@/lib/ai-gateway/providers/anthropic';
import {
CLAUDE_OPUS_FALLBACK_MODEL_ID,
Expand Down Expand Up @@ -49,6 +52,7 @@ import { isFreeModel } from '@/lib/ai-gateway/is-free-model';
import { isOpenAiModel } from '@/lib/ai-gateway/providers/openai';
import { ReasoningFormat } from '@/lib/ai-gateway/custom-llm/format';
import { ReasoningDetailType } from '@/lib/ai-gateway/custom-llm/reasoning-details';
import { getCustomPricing } from '@/lib/ai-gateway/custom-pricing';

function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === 'object' && value !== null && !Array.isArray(value);
Expand Down Expand Up @@ -223,6 +227,32 @@ export function applyAnthropicThinkingDefault(
}
}

export function removeUnsupportedRequestServiceTier(
requestedModel: string,
requestToMutate: GatewayRequest,
kiloExclusiveModel: KiloExclusiveModel | null
) {
const customPricing = getCustomPricing(requestedModel);
const reason =
customPricing && !customPricing.fallbackOnly
? 'non-fallback custom pricing'
: kiloExclusiveModel && !kiloExclusiveModel.flags.includes('flex')
? 'non-Flex Kilo-exclusive model'
: null;
const serviceTier = requestToMutate.body.service_tier;
if (!reason || serviceTier === undefined) {
return;
}

console.warn('[applyProviderSpecificLogic] Removed unsupported request-level service tier', {
model: requestedModel,
requestKind: requestToMutate.kind,
serviceTier,
reason,
});
delete requestToMutate.body.service_tier;
}

/**
* Inverse of the reasoning-content response transform: folds
* client-supplied `reasoning_details` back into the `reasoning_content` string
Expand Down Expand Up @@ -288,6 +318,7 @@ export async function applyProviderSpecificLogic(
enableReasoningSummaries(requestToMutate);

const kiloExclusiveModel = findKiloExclusiveModel(requestedModel);
removeUnsupportedRequestServiceTier(requestedModel, requestToMutate, kiloExclusiveModel);
if (kiloExclusiveModel) {
applyKiloExclusiveModelSettings(requestToMutate, kiloExclusiveModel);
}
Expand Down
24 changes: 24 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/kilo-exclusive-model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import {
type PricingTiers,
} from '@/lib/ai-gateway/providers/kilo-exclusive-model';
import type {
GatewayMessagesRequest,
GatewayRequest,
GatewayResponsesRequest,
OpenRouterChatCompletionRequest,
OpenRouterProviderConfig,
} from '@/lib/ai-gateway/providers/openrouter/types';
Expand Down Expand Up @@ -157,6 +159,28 @@ describe('applyKiloExclusiveModelSettings', () => {
expect(req.body.model).toBe('vendor/real-model');
});

it.each([
{
kind: 'chat_completions' as const,
body: { model: 'kilo/test-model', messages: [] } as OpenRouterChatCompletionRequest,
},
{
kind: 'responses' as const,
body: { model: 'kilo/test-model', input: '' } as GatewayResponsesRequest,
},
{
kind: 'messages' as const,
body: { model: 'kilo/test-model', max_tokens: 1, messages: [] } as GatewayMessagesRequest,
},
])('sets the Flex service tier for $kind requests', request => {
applyKiloExclusiveModelSettings(
request,
makeModel({ internal_id: 'vendor/real-model', flags: ['flex'] })
);

expect(request.body.service_tier).toBe('flex');
});

it('leaves provider untouched when there is no restriction', () => {
const req = makeRequest({ only: ['anthropic'], zdr: true });
applyKiloExclusiveModelSettings(req, makeModel({ internal_id: 'vendor/x' }));
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/kilo-exclusive-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
export type KiloExclusiveModelFlag =
| 'reasoning'
| 'vision'
| 'flex'
| 'stealth'
| 'vercel-routing'
| 'rate-limited'
Expand Down Expand Up @@ -169,6 +170,9 @@ export function applyKiloExclusiveModelSettings(
) {
requestToMutate.body.model = kiloExclusiveModel.internal_id;
removeNonSensicalMaxTokens(requestToMutate, kiloExclusiveModel);
if (kiloExclusiveModel.flags.includes('flex')) {
requestToMutate.body.service_tier = 'flex';
Comment thread
chrarnoldus marked this conversation as resolved.
}
const restriction = kiloExclusiveModel.inference_provider_restriction;
if (restriction.length === 0) {
return;
Expand Down
37 changes: 37 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/openai-exclusive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,40 @@ export const gpt_5_6_sol_discounted_model: KiloExclusiveModel = {
},
inference_provider_restriction: ['openai'],
};

export const gpt_6_astra_flex_model: KiloExclusiveModel = {
public_id: 'openai/gpt-6-astra-flex',
internal_id: 'openai/gpt-6-astra',
display_name: 'OpenAI: GPT-6 Astra Flex',
description:
'GPT-6 Astra with OpenAI Flex processing, offering lower costs in exchange for slower response times and occasional resource unavailability.',
status: 'disabled',
context_length: 1_050_000,
max_completion_tokens: 128_000,
gateway: 'vercel',
flags: ['reasoning', 'vision', 'flex'],
pricing: {
fallbackOnly: true,
tiers: [
{
start_context_length: 0,
pricing: {
prompt_per_million: 5,
completion_per_million: 25,
input_cache_read_per_million: 0.5,
input_cache_write_per_million: 6.25,
},
},
{
start_context_length: 272_000,
pricing: {
prompt_per_million: 10,
completion_per_million: 37.5,
input_cache_read_per_million: 1,
input_cache_write_per_million: 12.5,
},
},
],
},
inference_provider_restriction: ['openai'],
};
2 changes: 2 additions & 0 deletions apps/web/src/lib/ai-gateway/providers/vercel/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ export function convertProviderOptions(
);
})();

const serviceTier = requestToMutate.body.service_tier;
return {
gateway: {
only,
Expand All @@ -186,6 +187,7 @@ export function convertProviderOptions(
zeroDataRetention: provider?.zdr,
disallowPromptTraining: provider?.data_collection === 'deny' || undefined,
models: requestToMutate.body.models,
serviceTier: serviceTier === 'flex' || serviceTier === 'priority' ? serviceTier : undefined,
},
};
}
Expand Down
11 changes: 10 additions & 1 deletion apps/web/src/tests/openrouter-models-config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import {
CLAUDE_SONNET_CURRENT_MODEL_ID,
} from '@/lib/ai-gateway/providers/anthropic.constants';
import { GPT_CURRENT_MODEL_ID } from '@/lib/ai-gateway/providers/openai';
import { gpt_5_6_sol_discounted_model } from '@/lib/ai-gateway/providers/openai-exclusive';
import {
gpt_5_6_sol_discounted_model,
gpt_6_astra_flex_model,
} from '@/lib/ai-gateway/providers/openai-exclusive';
import {
GEMMA_4_26B_A4B_IT_ID,
gemma_4_26b_a4b_it_free_model,
Expand Down Expand Up @@ -59,6 +62,12 @@ describe('OpenRouter Models Config', () => {
} else {
expect(preferredModels).not.toContain(gpt_5_6_sol_discounted_model.public_id);
}

if (gpt_6_astra_flex_model.status === 'public') {
expect(preferredModels).toContain(gpt_6_astra_flex_model.public_id);
} else {
expect(preferredModels).not.toContain(gpt_6_astra_flex_model.public_id);
}
});

test('monitors only concrete preferred models', () => {
Expand Down
Loading