feat(llm): display cost next to the title

This commit is contained in:
Elian Doran
2026-03-29 21:29:27 +03:00
parent d1c206a05a
commit 293da1d4ef
4 changed files with 38 additions and 15 deletions

View File

@@ -5,16 +5,27 @@ interface FormDropdownList<T> extends Omit<DropdownProps, "children"> {
values: T[];
keyProperty: keyof T;
titleProperty: keyof T;
/** Property to show as a small suffix next to the title */
titleSuffixProperty?: keyof T;
descriptionProperty?: keyof T;
currentValue: string;
onChange(newValue: string): void;
}
export default function FormDropdownList<T>({ values, keyProperty, titleProperty, descriptionProperty, currentValue, onChange, ...restProps }: FormDropdownList<T>) {
export default function FormDropdownList<T>({ values, keyProperty, titleProperty, titleSuffixProperty, descriptionProperty, currentValue, onChange, ...restProps }: FormDropdownList<T>) {
const currentValueData = values.find(value => value[keyProperty] === currentValue);
const renderTitle = (item: T) => {
const title = item[titleProperty] as string;
const suffix = titleSuffixProperty ? item[titleSuffixProperty] as string : null;
if (suffix) {
return <>{title} <small>{suffix}</small></>;
}
return title;
};
return (
<Dropdown text={currentValueData?.[titleProperty] ?? ""} {...restProps}>
<Dropdown text={currentValueData ? renderTitle(currentValueData) : ""} {...restProps}>
{values.map(item => (
<FormListItem
onClick={() => onChange(item[keyProperty] as string)}
@@ -22,9 +33,9 @@ export default function FormDropdownList<T>({ values, keyProperty, titleProperty
description={descriptionProperty && item[descriptionProperty] as string}
selected={currentValue === item[keyProperty]}
>
{item[titleProperty] as string}
{renderTitle(item)}
</FormListItem>
))}
</Dropdown>
)
}
}

View File

@@ -1,7 +1,9 @@
import type { RefObject } from "preact";
import type { UseLlmChatReturn } from "./useLlmChat.js";
import { t } from "../../../services/i18n.js";
import FormDropdownList from "../../react/FormDropdownList.js";
import Dropdown from "../../react/Dropdown.js";
import { FormListItem } from "../../react/FormList.js";
import type { UseLlmChatReturn } from "./useLlmChat.js";
/** Format token count with thousands separators */
function formatTokenCount(tokens: number): string {
@@ -88,16 +90,21 @@ export default function ChatInputBar({
<div className="llm-chat-options">
<div className="llm-chat-model-selector">
<span className="bx bx-chip" />
<FormDropdownList
values={chat.availableModels}
keyProperty="id"
titleProperty="name"
descriptionProperty="costDescription"
currentValue={chat.selectedModel}
onChange={handleModelSelect}
<Dropdown
text={<>{currentModel?.name}</>}
disabled={chat.isStreaming}
buttonClassName="llm-chat-model-select"
/>
>
{chat.availableModels.map(model => (
<FormListItem
key={model.id}
onClick={() => handleModelSelect(model.id)}
checked={chat.selectedModel === model.id}
>
{model.name}<small>({model.costDescription})</small>
</FormListItem>
))}
</Dropdown>
</div>
<label className="llm-chat-toggle">
<input

View File

@@ -320,6 +320,11 @@
.llm-chat-model-selector .dropdown {
display: flex;
small {
margin-left: 0.5em;
color: var(--muted-text-color);
}
}
.llm-chat-model-select.select-button {

View File

@@ -96,7 +96,7 @@ export function useLlmChat(
getAvailableModels().then(models => {
const modelsWithDescription = models.map(m => ({
...m,
costDescription: m.costMultiplier ? `${m.costMultiplier}x cost` : undefined
costDescription: m.costMultiplier ? `${m.costMultiplier}x` : undefined
}));
setAvailableModels(modelsWithDescription);
if (!selectedModel) {