diff --git a/apps/web/src/components/organizations/providers-and-models/OrganizationProvidersAndModelsPage.tsx b/apps/web/src/components/organizations/providers-and-models/OrganizationProvidersAndModelsPage.tsx index ebffb1b0d9..f82f6f7713 100644 --- a/apps/web/src/components/organizations/providers-and-models/OrganizationProvidersAndModelsPage.tsx +++ b/apps/web/src/components/organizations/providers-and-models/OrganizationProvidersAndModelsPage.tsx @@ -35,6 +35,7 @@ import { modelRetainsPrompts, modelTrains, } from '@/lib/ai-gateway/providers/openrouter/model-data-policy'; +import { getModelDisplayPricing } from '@/lib/ai-gateway/providers/openrouter/display-pricing'; import { canManageOrganization } from '@kilocode/app-shared/organizations'; type Props = { @@ -273,6 +274,7 @@ export function OrganizationProvidersAndModelsPage({ organizationId, role }: Pro m => m.endpoint && normalizeModelId(m.slug) === infoModel.modelId ); if (!model || !model.endpoint) continue; + const pricing = getModelDisplayPricing(model.endpoint.pricing) ?? model.endpoint.pricing; offerings.push({ providerSlug: provider.slug, @@ -280,8 +282,8 @@ export function OrganizationProvidersAndModelsPage({ organizationId, role }: Pro providerIconUrl: provider.icon?.url ? normalizeProviderIconUrl(provider.icon.url) : null, trains: modelTrains(model, provider.dataPolicy.training), retainsPrompts: modelRetainsPrompts(model, provider.dataPolicy.retainsPrompts), - promptPrice: model.endpoint.pricing.prompt, - completionPrice: model.endpoint.pricing.completion, + promptPrice: pricing.prompt, + completionPrice: pricing.completion, }); } @@ -417,13 +419,14 @@ export function OrganizationProvidersAndModelsPage({ organizationId, role }: Pro if (!model.endpoint) continue; const normalizedModelId = normalizeModelId(model.slug); const sourceIndex = rows.length; + const pricing = getModelDisplayPricing(model.endpoint.pricing) ?? model.endpoint.pricing; rows.push({ modelId: normalizedModelId, modelName: model.name, preferredIndex: preferredIndexByModelId.get(normalizedModelId), sourceIndex, - promptPrice: model.endpoint.pricing.prompt, - completionPrice: model.endpoint.pricing.completion, + promptPrice: pricing.prompt, + completionPrice: pricing.completion, trains: modelTrains(model, provider.dataPolicy.training), retainsPrompts: modelRetainsPrompts(model, provider.dataPolicy.retainsPrompts), }); diff --git a/packages/db/src/schema-types.test.ts b/packages/db/src/schema-types.test.ts index ab4dc4b010..776919ebe7 100644 --- a/packages/db/src/schema-types.test.ts +++ b/packages/db/src/schema-types.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from '@jest/globals'; import { CodeReviewCouncilConfigSchema, ModelsSchema, + OpenRouterPricing, OrganizationSettingsSchema, StoredModelSchema, } from './schema-types'; @@ -128,6 +129,18 @@ describe('ModelsSchema', () => { }); }); +describe('OpenRouterPricing', () => { + it('preserves discounts used to calculate display pricing', () => { + const pricing = OpenRouterPricing.parse({ + prompt: '0.000000435', + completion: '0.00000087', + discount: 0.5, + }); + + expect(pricing.discount).toBe(0.5); + }); +}); + describe('StoredModelSchema', () => { it('includes model reasoning metadata', () => { const storedModel: StoredModel = { diff --git a/packages/db/src/schema-types.ts b/packages/db/src/schema-types.ts index 8e02785d90..7b3299fabf 100644 --- a/packages/db/src/schema-types.ts +++ b/packages/db/src/schema-types.ts @@ -1851,6 +1851,7 @@ export type OpenRouterPricing = z.infer; export const OpenRouterPricing = z.object({ prompt: z.string(), completion: z.string(), + discount: z.number().optional(), }); export type OpenRouterBaseModel = z.infer;