💄 style: improve model pricing with CNY (#5599)

* improve model pricing

* improve

* fix test
This commit is contained in:
Arvin Xu
2025-01-26 20:42:17 +08:00
committed by GitHub
parent 1794074eb3
commit 6d91457906
5 changed files with 37 additions and 49 deletions
@@ -11,12 +11,10 @@ import { ModelInfoTags } from '@/components/ModelSelect';
import { useIsMobile } from '@/hooks/useIsMobile';
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
import { AiModelSourceEnum, AiProviderModelListItem, ChatModelPricing } from '@/types/aiModel';
import { formatPriceByCurrency } from '@/utils/format';
import ModelConfigModal from './ModelConfigModal';
const f = (number: number | undefined, text: string) =>
typeof number !== 'undefined' ? text : undefined;
export const useStyles = createStyles(({ css, token, cx }) => {
const config = css`
opacity: 0;
@@ -74,7 +72,7 @@ const ModelItem = memo<ModelItemProps>(
type,
}) => {
const { styles } = useStyles();
const { t } = useTranslation(['modelProvider', 'components', 'models']);
const { t } = useTranslation(['modelProvider', 'components', 'models', 'common']);
const theme = useTheme();
const [activeAiProvider, isModelLoading, toggleModelEnabled, removeAiModel] = useAiInfraStore(
@@ -90,41 +88,43 @@ const ModelItem = memo<ModelItemProps>(
const [showConfig, setShowConfig] = useState(false);
const formatPricing = (): string[] => {
if (!pricing) return [];
switch (type) {
case 'chat': {
return [
f(
pricing?.input,
t('providerModels.item.pricing.inputTokens', { amount: pricing?.input }),
),
f(
pricing?.output,
t('providerModels.item.pricing.outputTokens', { amount: pricing?.output }),
),
typeof pricing.input === 'number' &&
t('providerModels.item.pricing.inputTokens', {
amount: formatPriceByCurrency(pricing.input, pricing?.currency),
}),
typeof pricing.output === 'number' &&
t('providerModels.item.pricing.outputTokens', {
amount: formatPriceByCurrency(pricing.output, pricing?.currency),
}),
].filter(Boolean) as string[];
}
case 'embedding': {
return [
f(
pricing?.input,
t('providerModels.item.pricing.inputTokens', { amount: pricing?.input }),
),
typeof pricing.input === 'number' &&
t('providerModels.item.pricing.inputTokens', {
amount: formatPriceByCurrency(pricing.input, pricing?.currency),
}),
].filter(Boolean) as string[];
}
case 'tts': {
return [
f(
pricing?.input,
t('providerModels.item.pricing.inputCharts', { amount: pricing?.input }),
),
typeof pricing.input === 'number' &&
t('providerModels.item.pricing.inputCharts', {
amount: formatPriceByCurrency(pricing.input, pricing?.currency),
}),
].filter(Boolean) as string[];
}
case 'stt': {
return [
f(
pricing?.input,
t('providerModels.item.pricing.inputMinutes', { amount: pricing?.input }),
),
typeof pricing.input === 'number' &&
t('providerModels.item.pricing.inputMinutes', {
amount: formatPriceByCurrency(pricing.input, pricing?.currency),
}),
].filter(Boolean) as string[];
}
@@ -142,7 +142,12 @@ const ModelItem = memo<ModelItemProps>(
releasedAt && t('providerModels.item.releasedAt', { releasedAt }),
...formatPricing(),
].filter(Boolean) as string[];
const { message, modal } = App.useApp();
const copyModelId = async () => {
await copyToClipboard(id);
message.success({ content: t('copySuccess', { ns: 'common' }) });
};
const isMobile = useIsMobile();
@@ -179,12 +184,7 @@ const ModelItem = memo<ModelItemProps>(
</Flexbox>
</Flexbox>
<div>
<Tag
onClick={() => {
copyToClipboard(id);
}}
style={{ cursor: 'pointer', marginRight: 0 }}
>
<Tag onClick={copyModelId} style={{ cursor: 'pointer', marginRight: 0 }}>
{id}
</Tag>
</div>
@@ -251,12 +251,7 @@ const ModelItem = memo<ModelItemProps>(
<Flexbox flex={1} gap={2} style={{ minWidth: 0 }}>
<Flexbox align={'center'} gap={8} horizontal>
{displayName || id}
<Tag
onClick={() => {
copyToClipboard(id);
}}
style={{ cursor: 'pointer', marginRight: 0 }}
>
<Tag onClick={copyModelId} style={{ cursor: 'pointer', marginRight: 0 }}>
{id}
</Tag>
<Flexbox className={styles.config} horizontal>
+2
View File
@@ -0,0 +1,2 @@
// in 2025.01.26
export const USD_TO_CNY = 7.24;
-2
View File
@@ -6,8 +6,6 @@ import {
DiscoverProviderItem,
} from '@/types/discover';
export const CNY_TO_USD = 7.14;
const DEFAULT_CREATED_AT = new Date().toISOString();
export const DEFAULT_DISCOVER_ASSISTANT_ITEM: Partial<DiscoverAssistantItem> = {
+2 -9
View File
@@ -1,7 +1,7 @@
import dayjs from 'dayjs';
import { describe, expect, it } from 'vitest';
import { CNY_TO_USD } from '@/const/discover';
import { USD_TO_CNY } from '@/const/currency';
import {
formatDate,
@@ -194,16 +194,9 @@ describe('format', () => {
expect(formatPriceByCurrency(1234.56, 'USD')).toBe('1,234.56');
});
it('should format CNY prices correctly', () => {
// Assuming CNY_TO_USD is 6.5
const CNY_TO_USD = 6.5;
expect(formatPriceByCurrency(1000, 'CNY')).toBe('140.06');
expect(formatPriceByCurrency(6500, 'CNY')).toBe('910.36');
});
it('should use the correct CNY_TO_USD conversion rate', () => {
const price = 1000;
const expectedCNY = formatPrice(price / CNY_TO_USD);
const expectedCNY = formatPrice(price / USD_TO_CNY);
expect(formatPriceByCurrency(price, 'CNY')).toBe(expectedCNY);
});
});
+2 -2
View File
@@ -2,7 +2,7 @@ import dayjs from 'dayjs';
import { isNumber } from 'lodash-es';
import numeral from 'numeral';
import { CNY_TO_USD } from '@/const/discover';
import { USD_TO_CNY } from '@/const/currency';
import { ModelPriceCurrency } from '@/types/llm';
export const formatSize = (bytes: number, fractionDigits: number = 1): string => {
@@ -118,7 +118,7 @@ export const formatPrice = (price: number, fractionDigits: number = 2) => {
export const formatPriceByCurrency = (price: number, currency?: ModelPriceCurrency) => {
if (currency === 'CNY') {
return formatPrice(price / CNY_TO_USD);
return formatPrice(price / USD_TO_CNY);
}
return formatPrice(price);
};