fix(ui): hide pricing section for unpriced models

Co-authored-by: kiloconnect[bot] <240665456+kiloconnect[bot]@users.noreply.github.com>
This commit is contained in:
chrarnoldus
2026-08-10 09:28:01 +00:00
co-authored by kiloconnect[bot]
parent 48c4a4af22
commit 08f8c5fc59
4 changed files with 29 additions and 24 deletions
@@ -2,6 +2,7 @@
"@kilocode/kilo-gateway": patch
"@kilocode/cli": patch
"kilo-code": patch
"@kilocode/kilo-jetbrains": patch
---
Ignore negative pricing entries from model catalogs and handle unpriced models gracefully in UI price formatting.
@@ -157,14 +157,14 @@ internal class ModelDetailsPanel(
return buildList {
item.releaseDate?.let { add(KiloBundle.message("model.picker.details.released") to date(it)) }
if (!item.free) {
item.cost?.let { cost ->
item.cost?.takeIf { it.input > 0.0 || it.output > 0.0 }?.let { cost ->
add(KiloBundle.message("model.picker.details.input") to price(cost.input))
add(KiloBundle.message("model.picker.details.output") to price(cost.output))
add(KiloBundle.message("model.picker.details.cached") to cached(cost.input, cost.cache?.read))
add(KiloBundle.message("model.picker.details.average") to price(average(cost.input, cost.output, cost.cache?.read)))
} ?: run {
item.inputPrice?.let { add(KiloBundle.message("model.picker.details.input") to price(it)) }
item.outputPrice?.let { add(KiloBundle.message("model.picker.details.output") to price(it)) }
item.inputPrice?.takeIf { it > 0.0 }?.let { add(KiloBundle.message("model.picker.details.input") to price(it)) }
item.outputPrice?.takeIf { it > 0.0 }?.let { add(KiloBundle.message("model.picker.details.output") to price(it)) }
}
}
ctx?.let { add(KiloBundle.message("model.picker.details.context") to context(it)) }
@@ -50,12 +50,13 @@ export const ModelPreview: Component<Props> = (props) => {
<Show when={m()}>
{(model) => {
const cost = () => model().cost
const hasPricing = () => cost() && (cost()!.input > 0 || cost()!.output > 0)
const bench = () => model().terminalBench
const cachedText = () => {
if (!cost()) return ""
if (!cost() || !hasPricing()) return ""
return fmtCachedPrice(cost()!) ?? language.t("model.preview.value.notSupported")
}
const avg = () => (cost() ? avgPrice(cost()!) : undefined)
const avg = () => (cost() && hasPricing() ? avgPrice(cost()!) : undefined)
const freeLabel = () => language.t("model.tag.free")
const dataLabel = () => freeDataLabel(language.t("model.tag.free"), language.t("model.tag.dataCollected"))
const autoLabel = () => autoSummary(model())
@@ -139,8 +140,8 @@ export const ModelPreview: Component<Props> = (props) => {
<span class="model-preview-value">{fmtDate(model().releaseDate!)}</span>
</Show>
{/* Pricing — hidden for free models */}
<Show when={cost() && !model().isFree}>
{/* Pricing — hidden for free models or models without fixed pricing */}
<Show when={cost() && !model().isFree && hasPricing()}>
<span class="model-preview-label">{language.t("model.preview.label.input")}</span>
<span class="model-preview-value">{fmtPrice(cost()!.input)}</span>
<span class="model-preview-label">{language.t("model.preview.label.output")}</span>
@@ -27,8 +27,9 @@ export function ModelInfoPanel(props: Props) {
const maxHeight = createMemo(() => Math.floor(dimensions().height / 2) - 3)
const cost = () => m().cost
const cached = () => (cost() ? fmtCachedPrice(cost()) : null)
const avg = () => (cost() ? avgPrice(cost()) : undefined)
const hasPricing = () => m().cost?.input > 0 || m().cost?.output > 0
const cached = () => (cost() && hasPricing() ? fmtCachedPrice(cost()) : null)
const avg = () => (cost() && hasPricing() ? avgPrice(cost()) : undefined)
const caps = () => m().capabilities
const inputs = () => caps()?.input
const outputs = () => caps()?.output
@@ -99,25 +100,27 @@ export function ModelInfoPanel(props: Props) {
</Show>
<Show when={!m().isFree}>
<box>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Input</text>
<text fg={theme.text}>{m() ? fmtPrice(m().cost.input) : "—"}</text>
</box>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Output</text>
<text fg={theme.text}>{m() ? fmtPrice(m().cost.output) : "—"}</text>
</box>
<Show when={cached()}>
<Show when={hasPricing()}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Cached</text>
<text fg={theme.text}>{cached()}</text>
<text fg={theme.textMuted}>Input</text>
<text fg={theme.text}>{m() ? fmtPrice(m().cost.input) : "—"}</text>
</box>
</Show>
<Show when={avg() !== undefined}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Avg Cost</text>
<text fg={theme.text}>{m() ? fmtPrice(avg()!) : "—"}</text>
<text fg={theme.textMuted}>Output</text>
<text fg={theme.text}>{m() ? fmtPrice(m().cost.output) : "—"}</text>
</box>
<Show when={cached()}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Cached</text>
<text fg={theme.text}>{cached()}</text>
</box>
</Show>
<Show when={avg() !== undefined}>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Avg Cost</text>
<text fg={theme.text}>{m() ? fmtPrice(avg()!) : "—"}</text>
</box>
</Show>
</Show>
<box flexDirection="row" justifyContent="space-between">
<text fg={theme.textMuted}>Context</text>