Skip to content

Commit 437e759

Browse files
committed
fix(models): address catalog review findings
1 parent 941272c commit 437e759

5 files changed

Lines changed: 78 additions & 46 deletions

File tree

‎apps/sim/app/(landing)/models/components/model-comparison-charts.tsx‎

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ const FEATURED_COMPARISON_PROVIDER_IDS = ['anthropic', 'openai', 'google']
1313

1414
/** Max latest models pulled from each featured provider. */
1515
const MAX_MODELS_PER_PROVIDER = 4
16+
const CHART_BAR_HEIGHT = 1
17+
const CHART_BAR_END_RADIUS_X = 1
18+
const CHART_BAR_END_RADIUS_Y = 3 / 28
1619

1720
const PROVIDER_ICON_MAP: Record<string, ComponentType<{ className?: string }>> = (() => {
1821
const map: Record<string, ComponentType<{ className?: string }>> = {}
@@ -24,6 +27,14 @@ const PROVIDER_ICON_MAP: Record<string, ComponentType<{ className?: string }>> =
2427
return map
2528
})()
2629

30+
function getRoundedRightBarPath(x: number, width: number): string {
31+
const right = x + width
32+
const radiusX = Math.min(CHART_BAR_END_RADIUS_X, width / 2)
33+
const radiusY = CHART_BAR_END_RADIUS_Y
34+
35+
return `M ${x} 0 H ${right - radiusX} Q ${right} 0 ${right} ${radiusY} V ${CHART_BAR_HEIGHT - radiusY} Q ${right} ${CHART_BAR_HEIGHT} ${right - radiusX} ${CHART_BAR_HEIGHT} H ${x} Z`
36+
}
37+
2738
function selectComparisonModels(models: CatalogModel[]): CatalogModel[] {
2839
const seen = new Set<string>()
2940
const result: CatalogModel[] = []
@@ -104,6 +115,9 @@ function StackedCostChart({ models }: ChartProps) {
104115
{data.entries.map(({ model, input, output, total }) => {
105116
const totalPct = data.maxTotal > 0 ? (total / data.maxTotal) * 100 : 0
106117
const inputPct = total > 0 ? (input / total) * 100 : 0
118+
const plottedTotalPct = Math.max(totalPct, 3)
119+
const plottedInputPct = (plottedTotalPct * inputPct) / 100
120+
const plottedOutputPct = plottedTotalPct - plottedInputPct
107121
const color = getProviderColor(model.providerId)
108122

109123
return (
@@ -115,27 +129,30 @@ function StackedCostChart({ models }: ChartProps) {
115129
<ModelLabel model={model} />
116130
<div className='flex h-7 min-w-0 flex-1 items-center gap-2.5'>
117131
<div className='hidden h-full min-w-0 flex-1 sm:block'>
118-
<div
119-
className='flex h-full overflow-hidden rounded-r-[3px]'
120-
style={{ width: `${Math.max(totalPct, 3)}%` }}
132+
<svg
133+
aria-hidden='true'
134+
className='size-full'
135+
preserveAspectRatio='none'
136+
viewBox='0 0 100 1'
121137
>
122-
<div
123-
className='h-full'
124-
style={{
125-
width: `${inputPct}%`,
126-
backgroundColor: color,
127-
opacity: 0.8,
128-
}}
129-
/>
130-
<div
131-
className='h-full'
132-
style={{
133-
width: `${100 - inputPct}%`,
134-
backgroundColor: color,
135-
opacity: 0.35,
136-
}}
137-
/>
138-
</div>
138+
{plottedInputPct > 0 &&
139+
(plottedOutputPct > 0 ? (
140+
<rect fill={color} fillOpacity={0.8} height='1' width={plottedInputPct} />
141+
) : (
142+
<path
143+
d={getRoundedRightBarPath(0, plottedInputPct)}
144+
fill={color}
145+
fillOpacity={0.8}
146+
/>
147+
))}
148+
{plottedOutputPct > 0 && (
149+
<path
150+
d={getRoundedRightBarPath(plottedInputPct, plottedOutputPct)}
151+
fill={color}
152+
fillOpacity={0.35}
153+
/>
154+
)}
155+
</svg>
139156
</div>
140157
<span className='shrink-0 text-[11px] text-[var(--text-muted)] sm:w-[148px] sm:text-xs'>
141158
{formatPrice(input)} input / {formatPrice(output)} output
@@ -188,14 +205,18 @@ function ContextWindowChart({ models }: ChartProps) {
188205
<ModelLabel model={model} />
189206
<div className='flex h-7 min-w-0 flex-1 items-center gap-2.5'>
190207
<div className='h-full min-w-0 flex-1'>
191-
<div
192-
className='h-full rounded-r-[3px]'
193-
style={{
194-
width: `${Math.max(pct, 3)}%`,
195-
backgroundColor: color,
196-
opacity: 0.8,
197-
}}
198-
/>
208+
<svg
209+
aria-hidden='true'
210+
className='size-full'
211+
preserveAspectRatio='none'
212+
viewBox='0 0 100 1'
213+
>
214+
<path
215+
d={getRoundedRightBarPath(0, Math.max(pct, 3))}
216+
fill={color}
217+
fillOpacity={0.8}
218+
/>
219+
</svg>
199220
</div>
200221
<span className='w-10 shrink-0 text-right text-[11px] text-[var(--text-muted)] tabular-nums sm:text-xs'>
201222
{formatTokenCount(value)}
@@ -232,11 +253,11 @@ export function ModelComparisonCharts({ models }: ModelComparisonChartsProps) {
232253

233254
<div className='h-px w-full bg-[var(--border)]' />
234255

235-
<div className='flex flex-col sm:flex-row'>
256+
<div className='flex flex-col lg:flex-row'>
236257
<div className='flex-1 p-6'>
237258
<StackedCostChart models={comparisonModels} />
238259
</div>
239-
<div className='h-px w-full bg-[var(--border)] sm:h-auto sm:w-px' />
260+
<div className='h-px w-full bg-[var(--border)] lg:h-auto lg:w-px' />
240261
<div className='flex-1 p-6'>
241262
<ContextWindowChart models={comparisonModels} />
242263
</div>

‎apps/sim/providers/cost-policy.test.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,16 @@ describe('priceModelUsage', () => {
138138
expect(cost).toMatchObject({ input: 0.800005, output: 1.8, total: 2.600005 })
139139
})
140140

141+
it('preserves zero-cost behavior for unregistered dynamic models', () => {
142+
const cost = priceModelUsage(
143+
'dynamic-provider/model',
144+
{ input: 300_000, output: 100_000 },
145+
LIST_PRICE_POLICY
146+
)
147+
148+
expect(cost).toMatchObject({ input: 0, output: 0, total: 0 })
149+
})
150+
141151
it('charges nothing when the policy is not billable', () => {
142152
const cost = priceModelUsage(
143153
PRICED_MODEL,

‎apps/sim/providers/cost-policy.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getCostMultiplier } from '@/lib/core/config/env-flags'
22
import type { NormalizedBlockOutput } from '@/executor/types'
3-
import { resolveModelTokenPricing } from '@/providers/pricing'
3+
import { getModelPricing, resolveModelTokenPricing } from '@/providers/pricing'
44
import type { ModelPricing } from '@/providers/types'
55
import { calculateCost, shouldBillModelUsage } from '@/providers/utils'
66

@@ -164,7 +164,7 @@ export function priceModelUsage(
164164

165165
const cacheRead = usage.cacheRead ?? 0
166166
const cacheWrites = (usage.cacheWrites ?? []).filter((write) => write.tokens > 0)
167-
const pricing = calculateCost(model, 0, 0).pricing
167+
const pricing = getModelPricing(model)
168168

169169
if (pricing) {
170170
const totalInputTokens =

‎apps/sim/providers/pricing.ts‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1+
import {
2+
getEmbeddingModelPricing,
3+
getModelPricing as getModelPricingFromDefinitions,
4+
} from '@/providers/models'
15
import type { ModelPricing, ModelTokenPricing } from '@/providers/types'
26

7+
/** Returns registered model pricing without applying the display-only fallback. */
8+
export function getModelPricing(modelId: string): ModelPricing | null {
9+
return getEmbeddingModelPricing(modelId) ?? getModelPricingFromDefinitions(modelId)
10+
}
11+
312
/** Resolves the token rates that apply to the full request input size. */
413
export function resolveModelTokenPricing(
514
pricing: ModelPricing,

‎apps/sim/providers/utils.ts‎

Lines changed: 6 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,9 @@ import type { SubBlockConfig } from '@/blocks/types'
2626
import { isCustomTool } from '@/executor/constants'
2727
import {
2828
getComputerUseModels,
29-
getEmbeddingModelPricing,
3029
getHostedModels as getHostedModelsFromDefinitions,
3130
getMaxOutputTokensForModel as getMaxOutputTokensForModelFromDefinitions,
3231
getMaxTemperature as getMaxTempFromDefinitions,
33-
getModelPricing as getModelPricingFromDefinitions,
3432
getModelsWithDeepResearch,
3533
getModelsWithoutMemory,
3634
getModelsWithPromptCaching,
@@ -51,7 +49,10 @@ import {
5149
supportsToolUsageControl as supportsToolUsageControlFromDefinitions,
5250
updateOllamaModels as updateOllamaModelsInDefinitions,
5351
} from '@/providers/models'
54-
import { resolveModelTokenPricing } from '@/providers/pricing'
52+
import {
53+
getModelPricing as getRegisteredModelPricing,
54+
resolveModelTokenPricing,
55+
} from '@/providers/pricing'
5556
import { collectToolResourceBindings, registerProviderToolBindings } from '@/providers/tool-binding'
5657
import {
5758
getProviderToolInputProvenance,
@@ -1010,11 +1011,7 @@ export function calculateCost(
10101011
inputMultiplier?: number,
10111012
outputMultiplier?: number
10121013
) {
1013-
let pricing = getEmbeddingModelPricing(model)
1014-
1015-
if (!pricing) {
1016-
pricing = getModelPricingFromDefinitions(model)
1017-
}
1014+
const pricing = getRegisteredModelPricing(model)
10181015

10191016
if (!pricing) {
10201017
const defaultPricing = {
@@ -1119,12 +1116,7 @@ export function sumToolCosts(toolResults?: Record<string, unknown>[]): number {
11191116
}
11201117

11211118
export function getModelPricing(modelId: string): ModelPricing | null {
1122-
const embeddingPricing = getEmbeddingModelPricing(modelId)
1123-
if (embeddingPricing) {
1124-
return embeddingPricing
1125-
}
1126-
1127-
return getModelPricingFromDefinitions(modelId)
1119+
return getRegisteredModelPricing(modelId)
11281120
}
11291121

11301122
/**

0 commit comments

Comments
 (0)