mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-20 06:15:58 +00:00
💄 style: improve model pricing with CNY (#5599)
* improve model pricing * improve * fix test
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
// in 2025.01.26
|
||||
export const USD_TO_CNY = 7.24;
|
||||
@@ -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> = {
|
||||
|
||||
@@ -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
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user