|
|
@@ -44,7 +44,10 @@ export function renderGroup(group) {
|
|
|
if (await copy(group)) {
|
|
|
showSuccess(i18next.t('已复制:') + group);
|
|
|
} else {
|
|
|
- Modal.error({ title: t('无法复制到剪贴板,请手动复制'), content: group });
|
|
|
+ Modal.error({
|
|
|
+ title: t('无法复制到剪贴板,请手动复制'),
|
|
|
+ content: group,
|
|
|
+ });
|
|
|
}
|
|
|
}}
|
|
|
>
|
|
|
@@ -64,28 +67,37 @@ export function renderRatio(ratio) {
|
|
|
} else if (ratio > 1) {
|
|
|
color = 'blue';
|
|
|
}
|
|
|
- return <Tag color={color}>{ratio}x {i18next.t('倍率')}</Tag>;
|
|
|
+ return (
|
|
|
+ <Tag color={color}>
|
|
|
+ {ratio}x {i18next.t('倍率')}
|
|
|
+ </Tag>
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
-const measureTextWidth = (text, style = {
|
|
|
- fontSize: '14px',
|
|
|
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
|
|
|
-}, containerWidth) => {
|
|
|
+const measureTextWidth = (
|
|
|
+ text,
|
|
|
+ style = {
|
|
|
+ fontSize: '14px',
|
|
|
+ fontFamily:
|
|
|
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
|
+ },
|
|
|
+ containerWidth,
|
|
|
+) => {
|
|
|
const span = document.createElement('span');
|
|
|
-
|
|
|
+
|
|
|
span.style.visibility = 'hidden';
|
|
|
span.style.position = 'absolute';
|
|
|
span.style.whiteSpace = 'nowrap';
|
|
|
span.style.fontSize = style.fontSize;
|
|
|
span.style.fontFamily = style.fontFamily;
|
|
|
-
|
|
|
+
|
|
|
span.textContent = text;
|
|
|
-
|
|
|
+
|
|
|
document.body.appendChild(span);
|
|
|
const width = span.offsetWidth;
|
|
|
-
|
|
|
+
|
|
|
document.body.removeChild(span);
|
|
|
-
|
|
|
+
|
|
|
return width;
|
|
|
};
|
|
|
|
|
|
@@ -94,7 +106,7 @@ export function truncateText(text, maxWidth = 200) {
|
|
|
return text;
|
|
|
}
|
|
|
if (!text) return text;
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
// Handle percentage-based maxWidth
|
|
|
let actualMaxWidth = maxWidth;
|
|
|
@@ -103,19 +115,19 @@ export function truncateText(text, maxWidth = 200) {
|
|
|
// Use window width as fallback container width
|
|
|
actualMaxWidth = window.innerWidth * percentage;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
const width = measureTextWidth(text);
|
|
|
if (width <= actualMaxWidth) return text;
|
|
|
-
|
|
|
+
|
|
|
let left = 0;
|
|
|
let right = text.length;
|
|
|
let result = text;
|
|
|
-
|
|
|
+
|
|
|
while (left <= right) {
|
|
|
const mid = Math.floor((left + right) / 2);
|
|
|
const truncated = text.slice(0, mid) + '...';
|
|
|
const currentWidth = measureTextWidth(truncated);
|
|
|
-
|
|
|
+
|
|
|
if (currentWidth <= actualMaxWidth) {
|
|
|
result = truncated;
|
|
|
left = mid + 1;
|
|
|
@@ -123,10 +135,13 @@ export function truncateText(text, maxWidth = 200) {
|
|
|
right = mid - 1;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
return result;
|
|
|
} catch (error) {
|
|
|
- console.warn('Text measurement failed, falling back to character count', error);
|
|
|
+ console.warn(
|
|
|
+ 'Text measurement failed, falling back to character count',
|
|
|
+ error,
|
|
|
+ );
|
|
|
if (text.length > 20) {
|
|
|
return text.slice(0, 17) + '...';
|
|
|
}
|
|
|
@@ -149,11 +164,11 @@ export const renderGroupOption = (item) => {
|
|
|
emptyContent,
|
|
|
...rest
|
|
|
} = item;
|
|
|
-
|
|
|
+
|
|
|
const baseStyle = {
|
|
|
- display: 'flex',
|
|
|
- justifyContent: 'space-between',
|
|
|
- alignItems: 'center',
|
|
|
+ display: 'flex',
|
|
|
+ justifyContent: 'space-between',
|
|
|
+ alignItems: 'center',
|
|
|
padding: '8px 16px',
|
|
|
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
|
backgroundColor: focused ? 'var(--semi-color-fill-0)' : 'transparent',
|
|
|
@@ -162,8 +177,8 @@ export const renderGroupOption = (item) => {
|
|
|
backgroundColor: 'var(--semi-color-primary-light-default)',
|
|
|
}),
|
|
|
'&:hover': {
|
|
|
- backgroundColor: !disabled && 'var(--semi-color-fill-1)'
|
|
|
- }
|
|
|
+ backgroundColor: !disabled && 'var(--semi-color-fill-1)',
|
|
|
+ },
|
|
|
};
|
|
|
|
|
|
const handleClick = () => {
|
|
|
@@ -177,9 +192,9 @@ export const renderGroupOption = (item) => {
|
|
|
onMouseEnter(e);
|
|
|
}
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
return (
|
|
|
- <div
|
|
|
+ <div
|
|
|
style={baseStyle}
|
|
|
onClick={handleClick}
|
|
|
onMouseEnter={handleMouseEnter}
|
|
|
@@ -188,7 +203,7 @@ export const renderGroupOption = (item) => {
|
|
|
<Typography.Text strong type={disabled ? 'tertiary' : undefined}>
|
|
|
{value}
|
|
|
</Typography.Text>
|
|
|
- <Typography.Text type="secondary" size="small">
|
|
|
+ <Typography.Text type='secondary' size='small'>
|
|
|
{label}
|
|
|
</Typography.Text>
|
|
|
</div>
|
|
|
@@ -222,8 +237,7 @@ export function renderQuotaNumberWithDigit(num, digits = 2) {
|
|
|
}
|
|
|
|
|
|
export function renderNumberWithPoint(num) {
|
|
|
- if (num === undefined)
|
|
|
- return '';
|
|
|
+ if (num === undefined) return '';
|
|
|
num = num.toFixed(2);
|
|
|
if (num >= 100000) {
|
|
|
// Convert number to string to manipulate it
|
|
|
@@ -302,11 +316,14 @@ export function renderModelPrice(
|
|
|
cacheRatio = 1.0,
|
|
|
) {
|
|
|
if (modelPrice !== -1) {
|
|
|
- return i18next.t('模型价格:${{price}} * 分组倍率:{{ratio}} = ${{total}}', {
|
|
|
- price: modelPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: modelPrice * groupRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型价格:${{price}} * 分组倍率:{{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ price: modelPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: modelPrice * groupRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
if (completionRatio === undefined) {
|
|
|
completionRatio = 0;
|
|
|
@@ -314,54 +331,72 @@ export function renderModelPrice(
|
|
|
let inputRatioPrice = modelRatio * 2.0;
|
|
|
let completionRatioPrice = modelRatio * 2.0 * completionRatio;
|
|
|
let cacheRatioPrice = modelRatio * 2.0 * cacheRatio;
|
|
|
-
|
|
|
+
|
|
|
// Calculate effective input tokens (non-cached + cached with ratio applied)
|
|
|
- const effectiveInputTokens = (inputTokens - cacheTokens) + (cacheTokens * cacheRatio);
|
|
|
-
|
|
|
+ const effectiveInputTokens =
|
|
|
+ inputTokens - cacheTokens + cacheTokens * cacheRatio;
|
|
|
+
|
|
|
let price =
|
|
|
(effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio +
|
|
|
(completionTokens / 1000000) * completionRatioPrice * groupRatio;
|
|
|
-
|
|
|
+
|
|
|
return (
|
|
|
<>
|
|
|
<article>
|
|
|
- <p>{i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
- price: inputRatioPrice,
|
|
|
- })}</p>
|
|
|
- <p>{i18next.t('补全价格:${{price}} * {{completionRatio}} = ${{total}} / 1M tokens (补全倍率: {{completionRatio}})', {
|
|
|
- price: inputRatioPrice,
|
|
|
- total: completionRatioPrice,
|
|
|
- completionRatio: completionRatio
|
|
|
- })}</p>
|
|
|
- {cacheTokens > 0 && (
|
|
|
- <p>{i18next.t('缓存价格:${{price}} * {{cacheRatio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})', {
|
|
|
+ <p>
|
|
|
+ {i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
price: inputRatioPrice,
|
|
|
- total: inputRatioPrice * cacheRatio,
|
|
|
- cacheRatio: cacheRatio
|
|
|
- })}</p>
|
|
|
+ })}
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '补全价格:${{price}} * {{completionRatio}} = ${{total}} / 1M tokens (补全倍率: {{completionRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ total: completionRatioPrice,
|
|
|
+ completionRatio: completionRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
+ {cacheTokens > 0 && (
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '缓存价格:${{price}} * {{cacheRatio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ total: inputRatioPrice * cacheRatio,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
)}
|
|
|
<p></p>
|
|
|
<p>
|
|
|
- {cacheTokens > 0 ?
|
|
|
- i18next.t('提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', {
|
|
|
- nonCacheInput: inputTokens - cacheTokens,
|
|
|
- cacheInput: cacheTokens,
|
|
|
- cachePrice: inputRatioPrice * cacheRatio,
|
|
|
- price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: price.toFixed(6)
|
|
|
- }) :
|
|
|
- i18next.t('提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', {
|
|
|
- input: inputTokens,
|
|
|
- price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: price.toFixed(6)
|
|
|
- })
|
|
|
- }
|
|
|
+ {cacheTokens > 0
|
|
|
+ ? i18next.t(
|
|
|
+ '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ nonCacheInput: inputTokens - cacheTokens,
|
|
|
+ cacheInput: cacheTokens,
|
|
|
+ cachePrice: inputRatioPrice * cacheRatio,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: price.toFixed(6),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ : i18next.t(
|
|
|
+ '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ input: inputTokens,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: price.toFixed(6),
|
|
|
+ },
|
|
|
+ )}
|
|
|
</p>
|
|
|
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
|
|
|
</article>
|
|
|
@@ -380,19 +415,22 @@ export function renderModelPriceSimple(
|
|
|
if (modelPrice !== -1) {
|
|
|
return i18next.t('价格:${{price}} * 分组:{{ratio}}', {
|
|
|
price: modelPrice,
|
|
|
- ratio: groupRatio
|
|
|
+ ratio: groupRatio,
|
|
|
});
|
|
|
} else {
|
|
|
if (cacheTokens !== 0) {
|
|
|
- return i18next.t('模型: {{ratio}} * 分组: {{groupRatio}} * 缓存: {{cacheRatio}}', {
|
|
|
- ratio: modelRatio,
|
|
|
- groupRatio: groupRatio,
|
|
|
- cacheRatio: cacheRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型: {{ratio}} * 分组: {{groupRatio}} * 缓存: {{cacheRatio}}',
|
|
|
+ {
|
|
|
+ ratio: modelRatio,
|
|
|
+ groupRatio: groupRatio,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
return i18next.t('模型: {{ratio}} * 分组: {{groupRatio}}', {
|
|
|
ratio: modelRatio,
|
|
|
- groupRatio: groupRatio
|
|
|
+ groupRatio: groupRatio,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
@@ -414,11 +452,14 @@ export function renderAudioModelPrice(
|
|
|
) {
|
|
|
// 1 ratio = $0.002 / 1K tokens
|
|
|
if (modelPrice !== -1) {
|
|
|
- return i18next.t('模型价格:${{price}} * 分组倍率:{{ratio}} = ${{total}}', {
|
|
|
- price: modelPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: modelPrice * groupRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型价格:${{price}} * 分组倍率:{{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ price: modelPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: modelPrice * groupRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
if (completionRatio === undefined) {
|
|
|
completionRatio = 0;
|
|
|
@@ -430,81 +471,120 @@ export function renderAudioModelPrice(
|
|
|
let inputRatioPrice = modelRatio * 2.0;
|
|
|
let completionRatioPrice = modelRatio * 2.0 * completionRatio;
|
|
|
let cacheRatioPrice = modelRatio * 2.0 * cacheRatio;
|
|
|
-
|
|
|
+
|
|
|
// Calculate effective input tokens (non-cached + cached with ratio applied)
|
|
|
- const effectiveInputTokens = (inputTokens - cacheTokens) + (cacheTokens * cacheRatio);
|
|
|
-
|
|
|
+ const effectiveInputTokens =
|
|
|
+ inputTokens - cacheTokens + cacheTokens * cacheRatio;
|
|
|
+
|
|
|
let textPrice =
|
|
|
(effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio +
|
|
|
- (completionTokens / 1000000) * completionRatioPrice * groupRatio
|
|
|
+ (completionTokens / 1000000) * completionRatioPrice * groupRatio;
|
|
|
let audioPrice =
|
|
|
(audioInputTokens / 1000000) * inputRatioPrice * audioRatio * groupRatio +
|
|
|
- (audioCompletionTokens / 1000000) * inputRatioPrice * audioRatio * audioCompletionRatio * groupRatio;
|
|
|
+ (audioCompletionTokens / 1000000) *
|
|
|
+ inputRatioPrice *
|
|
|
+ audioRatio *
|
|
|
+ audioCompletionRatio *
|
|
|
+ groupRatio;
|
|
|
let price = textPrice + audioPrice;
|
|
|
return (
|
|
|
<>
|
|
|
<article>
|
|
|
- <p>{i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
- price: inputRatioPrice,
|
|
|
- })}</p>
|
|
|
- <p>{i18next.t('补全价格:${{price}} * {{completionRatio}} = ${{total}} / 1M tokens (补全倍率: {{completionRatio}})', {
|
|
|
- price: inputRatioPrice,
|
|
|
- total: completionRatioPrice,
|
|
|
- completionRatio: completionRatio
|
|
|
- })}</p>
|
|
|
- {cacheTokens > 0 && (
|
|
|
- <p>{i18next.t('缓存价格:${{price}} * {{cacheRatio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})', {
|
|
|
+ <p>
|
|
|
+ {i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
price: inputRatioPrice,
|
|
|
- total: inputRatioPrice * cacheRatio,
|
|
|
- cacheRatio: cacheRatio
|
|
|
- })}</p>
|
|
|
+ })}
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '补全价格:${{price}} * {{completionRatio}} = ${{total}} / 1M tokens (补全倍率: {{completionRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ total: completionRatioPrice,
|
|
|
+ completionRatio: completionRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
+ {cacheTokens > 0 && (
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '缓存价格:${{price}} * {{cacheRatio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ total: inputRatioPrice * cacheRatio,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
)}
|
|
|
- <p>{i18next.t('音频提示价格:${{price}} * {{audioRatio}} = ${{total}} / 1M tokens (音频倍率: {{audioRatio}})', {
|
|
|
- price: inputRatioPrice,
|
|
|
- total: inputRatioPrice * audioRatio,
|
|
|
- audioRatio: audioRatio
|
|
|
- })}</p>
|
|
|
- <p>{i18next.t('音频补全价格:${{price}} * {{audioRatio}} * {{audioCompRatio}} = ${{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})', {
|
|
|
- price: inputRatioPrice,
|
|
|
- total: inputRatioPrice * audioRatio * audioCompletionRatio,
|
|
|
- audioRatio: audioRatio,
|
|
|
- audioCompRatio: audioCompletionRatio
|
|
|
- })}</p>
|
|
|
<p>
|
|
|
- {cacheTokens > 0 ?
|
|
|
- i18next.t('文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}', {
|
|
|
- nonCacheInput: inputTokens - cacheTokens,
|
|
|
- cacheInput: cacheTokens,
|
|
|
- cachePrice: inputRatioPrice * cacheRatio,
|
|
|
+ {i18next.t(
|
|
|
+ '音频提示价格:${{price}} * {{audioRatio}} = ${{total}} / 1M tokens (音频倍率: {{audioRatio}})',
|
|
|
+ {
|
|
|
price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- total: textPrice.toFixed(6)
|
|
|
- }) :
|
|
|
- i18next.t('文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}', {
|
|
|
- input: inputTokens,
|
|
|
+ total: inputRatioPrice * audioRatio,
|
|
|
+ audioRatio: audioRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '音频补全价格:${{price}} * {{audioRatio}} * {{audioCompRatio}} = ${{total}} / 1M tokens (音频补全倍率: {{audioCompRatio}})',
|
|
|
+ {
|
|
|
price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- total: textPrice.toFixed(6)
|
|
|
- })
|
|
|
- }
|
|
|
+ total: inputRatioPrice * audioRatio * audioCompletionRatio,
|
|
|
+ audioRatio: audioRatio,
|
|
|
+ audioCompRatio: audioCompletionRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
</p>
|
|
|
<p>
|
|
|
- {i18next.t('音频提示 {{input}} tokens / 1M tokens * ${{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * ${{audioCompPrice}} = ${{total}}', {
|
|
|
- input: audioInputTokens,
|
|
|
- completion: audioCompletionTokens,
|
|
|
- audioInputPrice: audioRatio * inputRatioPrice,
|
|
|
- audioCompPrice: audioRatio * audioCompletionRatio * inputRatioPrice,
|
|
|
- total: audioPrice.toFixed(6)
|
|
|
- })}
|
|
|
+ {cacheTokens > 0
|
|
|
+ ? i18next.t(
|
|
|
+ '文字提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
|
|
+ {
|
|
|
+ nonCacheInput: inputTokens - cacheTokens,
|
|
|
+ cacheInput: cacheTokens,
|
|
|
+ cachePrice: inputRatioPrice * cacheRatio,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ total: textPrice.toFixed(6),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ : i18next.t(
|
|
|
+ '文字提示 {{input}} tokens / 1M tokens * ${{price}} + 文字补全 {{completion}} tokens / 1M tokens * ${{compPrice}} = ${{total}}',
|
|
|
+ {
|
|
|
+ input: inputTokens,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ total: textPrice.toFixed(6),
|
|
|
+ },
|
|
|
+ )}
|
|
|
</p>
|
|
|
<p>
|
|
|
- {i18next.t('总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = ${{total}}', {
|
|
|
- total: price.toFixed(6),
|
|
|
- textPrice: textPrice.toFixed(6),
|
|
|
- audioPrice: audioPrice.toFixed(6)
|
|
|
- })}
|
|
|
+ {i18next.t(
|
|
|
+ '音频提示 {{input}} tokens / 1M tokens * ${{audioInputPrice}} + 音频补全 {{completion}} tokens / 1M tokens * ${{audioCompPrice}} = ${{total}}',
|
|
|
+ {
|
|
|
+ input: audioInputTokens,
|
|
|
+ completion: audioCompletionTokens,
|
|
|
+ audioInputPrice: audioRatio * inputRatioPrice,
|
|
|
+ audioCompPrice:
|
|
|
+ audioRatio * audioCompletionRatio * inputRatioPrice,
|
|
|
+ total: audioPrice.toFixed(6),
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '总价:文字价格 {{textPrice}} + 音频价格 {{audioPrice}} = ${{total}}',
|
|
|
+ {
|
|
|
+ total: price.toFixed(6),
|
|
|
+ textPrice: textPrice.toFixed(6),
|
|
|
+ audioPrice: audioPrice.toFixed(6),
|
|
|
+ },
|
|
|
+ )}
|
|
|
</p>
|
|
|
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
|
|
|
</article>
|
|
|
@@ -517,7 +597,9 @@ export function renderQuotaWithPrompt(quota, digits) {
|
|
|
let displayInCurrency = localStorage.getItem('display_in_currency');
|
|
|
displayInCurrency = displayInCurrency === 'true';
|
|
|
if (displayInCurrency) {
|
|
|
- return ' | ' + i18next.t('等价金额') + ': ' + renderQuota(quota, digits) + '';
|
|
|
+ return (
|
|
|
+ ' | ' + i18next.t('等价金额') + ': ' + renderQuota(quota, digits) + ''
|
|
|
+ );
|
|
|
}
|
|
|
return '';
|
|
|
}
|
|
|
@@ -537,7 +619,7 @@ const colors = [
|
|
|
'red',
|
|
|
'teal',
|
|
|
'violet',
|
|
|
- 'yellow'
|
|
|
+ 'yellow',
|
|
|
];
|
|
|
|
|
|
// 基础10色色板 (N ≤ 10)
|
|
|
@@ -551,7 +633,7 @@ const baseColors = [
|
|
|
'#304D77',
|
|
|
'#B48DEB',
|
|
|
'#009488',
|
|
|
- '#FF7DDA'
|
|
|
+ '#FF7DDA',
|
|
|
];
|
|
|
|
|
|
// 扩展20色色板 (10 < N ≤ 20)
|
|
|
@@ -575,7 +657,7 @@ const extendedColors = [
|
|
|
'#009488',
|
|
|
'#59BAA8',
|
|
|
'#FF7DDA',
|
|
|
- '#FFCFEE'
|
|
|
+ '#FFCFEE',
|
|
|
];
|
|
|
|
|
|
export const modelColorMap = {
|
|
|
@@ -631,14 +713,14 @@ export function modelToColor(modelName) {
|
|
|
// 2. 生成一个稳定的数字作为索引
|
|
|
let hash = 0;
|
|
|
for (let i = 0; i < modelName.length; i++) {
|
|
|
- hash = ((hash << 5) - hash) + modelName.charCodeAt(i);
|
|
|
+ hash = (hash << 5) - hash + modelName.charCodeAt(i);
|
|
|
hash = hash & hash; // Convert to 32-bit integer
|
|
|
}
|
|
|
hash = Math.abs(hash);
|
|
|
|
|
|
// 3. 根据模型名称长度选择不同的色板
|
|
|
const colorPalette = modelName.length > 10 ? extendedColors : baseColors;
|
|
|
-
|
|
|
+
|
|
|
// 4. 使用hash值选择颜色
|
|
|
const index = hash % colorPalette.length;
|
|
|
return colorPalette[index];
|
|
|
@@ -668,12 +750,15 @@ export function renderClaudeModelPrice(
|
|
|
const ratioLabel = false ? i18next.t('专属倍率') : i18next.t('分组倍率');
|
|
|
|
|
|
if (modelPrice !== -1) {
|
|
|
- return i18next.t('模型价格:${{price}} * {{ratioType}}:{{ratio}} = ${{total}}', {
|
|
|
- price: modelPrice,
|
|
|
- ratioType: ratioLabel,
|
|
|
- ratio: groupRatio,
|
|
|
- total: modelPrice * groupRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型价格:${{price}} * {{ratioType}}:{{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ price: modelPrice,
|
|
|
+ ratioType: ratioLabel,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: modelPrice * groupRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
if (completionRatio === undefined) {
|
|
|
completionRatio = 0;
|
|
|
@@ -687,9 +772,10 @@ export function renderClaudeModelPrice(
|
|
|
|
|
|
// Calculate effective input tokens (non-cached + cached with ratio applied + cache creation with ratio applied)
|
|
|
const nonCachedTokens = inputTokens;
|
|
|
- const effectiveInputTokens = nonCachedTokens +
|
|
|
- (cacheTokens * cacheRatio) +
|
|
|
- (cacheCreationTokens * cacheCreationRatio);
|
|
|
+ const effectiveInputTokens =
|
|
|
+ nonCachedTokens +
|
|
|
+ cacheTokens * cacheRatio +
|
|
|
+ cacheCreationTokens * cacheCreationRatio;
|
|
|
|
|
|
let price =
|
|
|
(effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio +
|
|
|
@@ -698,56 +784,78 @@ export function renderClaudeModelPrice(
|
|
|
return (
|
|
|
<>
|
|
|
<article>
|
|
|
- <p>{i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
- price: inputRatioPrice,
|
|
|
- })}</p>
|
|
|
- <p>{i18next.t('补全价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens', {
|
|
|
- price: inputRatioPrice,
|
|
|
- ratio: completionRatio,
|
|
|
- total: completionRatioPrice
|
|
|
- })}</p>
|
|
|
- {cacheTokens > 0 && (
|
|
|
- <p>{i18next.t('缓存价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})', {
|
|
|
+ <p>
|
|
|
+ {i18next.t('提示价格:${{price}} / 1M tokens', {
|
|
|
price: inputRatioPrice,
|
|
|
- ratio: cacheRatio,
|
|
|
- total: cacheRatioPrice,
|
|
|
- cacheRatio: cacheRatio
|
|
|
- })}</p>
|
|
|
+ })}
|
|
|
+ </p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '补全价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ ratio: completionRatio,
|
|
|
+ total: completionRatioPrice,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
+ {cacheTokens > 0 && (
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '缓存价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens (缓存倍率: {{cacheRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ ratio: cacheRatio,
|
|
|
+ total: cacheRatioPrice,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
)}
|
|
|
{cacheCreationTokens > 0 && (
|
|
|
- <p>{i18next.t('缓存创建价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})', {
|
|
|
- price: inputRatioPrice,
|
|
|
- ratio: cacheCreationRatio,
|
|
|
- total: cacheCreationRatioPrice,
|
|
|
- cacheCreationRatio: cacheCreationRatio
|
|
|
- })}</p>
|
|
|
+ <p>
|
|
|
+ {i18next.t(
|
|
|
+ '缓存创建价格:${{price}} * {{ratio}} = ${{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})',
|
|
|
+ {
|
|
|
+ price: inputRatioPrice,
|
|
|
+ ratio: cacheCreationRatio,
|
|
|
+ total: cacheCreationRatioPrice,
|
|
|
+ cacheCreationRatio: cacheCreationRatio,
|
|
|
+ },
|
|
|
+ )}
|
|
|
+ </p>
|
|
|
)}
|
|
|
<p></p>
|
|
|
<p>
|
|
|
- {(cacheTokens > 0 || cacheCreationTokens > 0) ?
|
|
|
- i18next.t('提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', {
|
|
|
- nonCacheInput: nonCachedTokens,
|
|
|
- cacheInput: cacheTokens,
|
|
|
- cacheRatio: cacheRatio,
|
|
|
- cacheCreationInput: cacheCreationTokens,
|
|
|
- cacheCreationRatio: cacheCreationRatio,
|
|
|
- cachePrice: cacheRatioPrice,
|
|
|
- cacheCreationPrice: cacheCreationRatioPrice,
|
|
|
- price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: price.toFixed(6)
|
|
|
- }) :
|
|
|
- i18next.t('提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}', {
|
|
|
- input: inputTokens,
|
|
|
- price: inputRatioPrice,
|
|
|
- completion: completionTokens,
|
|
|
- compPrice: completionRatioPrice,
|
|
|
- ratio: groupRatio,
|
|
|
- total: price.toFixed(6)
|
|
|
- })
|
|
|
- }
|
|
|
+ {cacheTokens > 0 || cacheCreationTokens > 0
|
|
|
+ ? i18next.t(
|
|
|
+ '提示 {{nonCacheInput}} tokens / 1M tokens * ${{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * ${{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * ${{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ nonCacheInput: nonCachedTokens,
|
|
|
+ cacheInput: cacheTokens,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ cacheCreationInput: cacheCreationTokens,
|
|
|
+ cacheCreationRatio: cacheCreationRatio,
|
|
|
+ cachePrice: cacheRatioPrice,
|
|
|
+ cacheCreationPrice: cacheCreationRatioPrice,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: price.toFixed(6),
|
|
|
+ },
|
|
|
+ )
|
|
|
+ : i18next.t(
|
|
|
+ '提示 {{input}} tokens / 1M tokens * ${{price}} + 补全 {{completion}} tokens / 1M tokens * ${{compPrice}} * 分组 {{ratio}} = ${{total}}',
|
|
|
+ {
|
|
|
+ input: inputTokens,
|
|
|
+ price: inputRatioPrice,
|
|
|
+ completion: completionTokens,
|
|
|
+ compPrice: completionRatioPrice,
|
|
|
+ ratio: groupRatio,
|
|
|
+ total: price.toFixed(6),
|
|
|
+ },
|
|
|
+ )}
|
|
|
</p>
|
|
|
<p>{i18next.t('仅供参考,以实际扣费为准')}</p>
|
|
|
</article>
|
|
|
@@ -770,17 +878,20 @@ export function renderClaudeLogContent(
|
|
|
return i18next.t('模型价格 ${{price}},{{ratioType}} {{ratio}}', {
|
|
|
price: modelPrice,
|
|
|
ratioType: ratioLabel,
|
|
|
- ratio: groupRatio
|
|
|
+ ratio: groupRatio,
|
|
|
});
|
|
|
} else {
|
|
|
- return i18next.t('模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}},{{ratioType}} {{ratio}}', {
|
|
|
- modelRatio: modelRatio,
|
|
|
- completionRatio: completionRatio,
|
|
|
- cacheRatio: cacheRatio,
|
|
|
- cacheCreationRatio: cacheCreationRatio,
|
|
|
- ratioType: ratioLabel,
|
|
|
- ratio: groupRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}},{{ratioType}} {{ratio}}',
|
|
|
+ {
|
|
|
+ modelRatio: modelRatio,
|
|
|
+ completionRatio: completionRatio,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ cacheCreationRatio: cacheCreationRatio,
|
|
|
+ ratioType: ratioLabel,
|
|
|
+ ratio: groupRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -799,22 +910,25 @@ export function renderClaudeModelPriceSimple(
|
|
|
return i18next.t('价格:${{price}} * {{ratioType}}:{{ratio}}', {
|
|
|
price: modelPrice,
|
|
|
ratioType: ratioLabel,
|
|
|
- ratio: groupRatio
|
|
|
+ ratio: groupRatio,
|
|
|
});
|
|
|
} else {
|
|
|
if (cacheTokens !== 0 || cacheCreationTokens !== 0) {
|
|
|
- return i18next.t('模型: {{ratio}} * {{ratioType}}: {{groupRatio}} * 缓存: {{cacheRatio}}', {
|
|
|
- ratio: modelRatio,
|
|
|
- ratioType: ratioLabel,
|
|
|
- groupRatio: groupRatio,
|
|
|
- cacheRatio: cacheRatio,
|
|
|
- cacheCreationRatio: cacheCreationRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型: {{ratio}} * {{ratioType}}: {{groupRatio}} * 缓存: {{cacheRatio}}',
|
|
|
+ {
|
|
|
+ ratio: modelRatio,
|
|
|
+ ratioType: ratioLabel,
|
|
|
+ groupRatio: groupRatio,
|
|
|
+ cacheRatio: cacheRatio,
|
|
|
+ cacheCreationRatio: cacheCreationRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
} else {
|
|
|
return i18next.t('模型: {{ratio}} * {{ratioType}}: {{groupRatio}}', {
|
|
|
ratio: modelRatio,
|
|
|
ratioType: ratioLabel,
|
|
|
- groupRatio: groupRatio
|
|
|
+ groupRatio: groupRatio,
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
@@ -824,7 +938,7 @@ export function renderLogContent(
|
|
|
modelRatio,
|
|
|
completionRatio,
|
|
|
modelPrice = -1,
|
|
|
- groupRatio
|
|
|
+ groupRatio,
|
|
|
) {
|
|
|
const ratioLabel = false ? i18next.t('专属倍率') : i18next.t('分组倍率');
|
|
|
|
|
|
@@ -832,14 +946,17 @@ export function renderLogContent(
|
|
|
return i18next.t('模型价格 ${{price}},{{ratioType}} {{ratio}}', {
|
|
|
price: modelPrice,
|
|
|
ratioType: ratioLabel,
|
|
|
- ratio: groupRatio
|
|
|
+ ratio: groupRatio,
|
|
|
});
|
|
|
} else {
|
|
|
- return i18next.t('模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},{{ratioType}} {{ratio}}', {
|
|
|
- modelRatio: modelRatio,
|
|
|
- completionRatio: completionRatio,
|
|
|
- ratioType: ratioLabel,
|
|
|
- ratio: groupRatio
|
|
|
- });
|
|
|
+ return i18next.t(
|
|
|
+ '模型倍率 {{modelRatio}},补全倍率 {{completionRatio}},{{ratioType}} {{ratio}}',
|
|
|
+ {
|
|
|
+ modelRatio: modelRatio,
|
|
|
+ completionRatio: completionRatio,
|
|
|
+ ratioType: ratioLabel,
|
|
|
+ ratio: groupRatio,
|
|
|
+ },
|
|
|
+ );
|
|
|
}
|
|
|
}
|