feat: enhance tiered billing logic and improve variable handling in pricing calculations
This commit is contained in:
+21
-99
@@ -20,6 +20,7 @@ For commercial licensing, please contact support@quantumnous.com
|
||||
import React from 'react';
|
||||
import { Card, Avatar, Tag, Table, Typography } from '@douyinfe/semi-ui';
|
||||
import { IconPriceTag } from '@douyinfe/semi-icons';
|
||||
import { parseTiersFromExpr } from '../../../../../helpers';
|
||||
import {
|
||||
splitBillingExprAndRequestRules,
|
||||
tryParseRequestRuleExpr,
|
||||
@@ -36,15 +37,6 @@ const { Text } = Typography;
|
||||
|
||||
const PRICE_SUFFIX = '$/1M tokens';
|
||||
|
||||
function unitCostToPrice(uc) {
|
||||
return Number(uc) || 0;
|
||||
}
|
||||
|
||||
function formatPrice(uc) {
|
||||
const p = unitCostToPrice(uc);
|
||||
return p ? `$${p.toFixed(4)}` : '-';
|
||||
}
|
||||
|
||||
const VAR_LABELS = { p: '输入', c: '输出' };
|
||||
const OP_LABELS = { '<': '<', '<=': '≤', '>': '>', '>=': '≥' };
|
||||
const TIME_FUNC_LABELS = { hour: '小时', minute: '分钟', weekday: '星期', month: '月份', day: '日期' };
|
||||
@@ -71,54 +63,6 @@ function formatConditionSummary(conditions, t) {
|
||||
.join(' && ');
|
||||
}
|
||||
|
||||
function tryParseTiers(baseExpr) {
|
||||
if (!baseExpr) return null;
|
||||
try {
|
||||
const cacheVars = ['cr', 'cc', 'cc1h'];
|
||||
const optCache = cacheVars.map((v) => `(?:\\s*\\+\\s*${v}\\s*\\*\\s*([\\d.eE+-]+))?`).join('');
|
||||
const bodyPat = `p\\s*\\*\\s*([\\d.eE+-]+)\\s*\\+\\s*c\\s*\\*\\s*([\\d.eE+-]+)${optCache}`;
|
||||
const singleRe = new RegExp(`^tier\\("([^"]*)",\\s*${bodyPat}\\)$`);
|
||||
const simple = baseExpr.match(singleRe);
|
||||
if (simple) {
|
||||
return [{
|
||||
label: simple[1],
|
||||
conditions: [],
|
||||
inputPrice: unitCostToPrice(Number(simple[2])),
|
||||
outputPrice: unitCostToPrice(Number(simple[3])),
|
||||
cacheReadPrice: simple[4] ? unitCostToPrice(Number(simple[4])) : null,
|
||||
cacheCreatePrice: simple[5] ? unitCostToPrice(Number(simple[5])) : null,
|
||||
cacheCreate1hPrice: simple[6] ? unitCostToPrice(Number(simple[6])) : null,
|
||||
}];
|
||||
}
|
||||
|
||||
const condGroup = `((?:(?:p|c)\\s*(?:<|<=|>|>=)\\s*[\\d.eE+]+)(?:\\s*&&\\s*(?:p|c)\\s*(?:<|<=|>|>=)\\s*[\\d.eE+]+)*)`;
|
||||
const tierRe = new RegExp(`(?:${condGroup}\\s*\\?\\s*)?tier\\("([^"]*)",\\s*${bodyPat}\\)`, 'g');
|
||||
const tiers = [];
|
||||
let match;
|
||||
while ((match = tierRe.exec(baseExpr)) !== null) {
|
||||
const condStr = match[1] || '';
|
||||
const conditions = [];
|
||||
if (condStr) {
|
||||
for (const cp of condStr.split(/\s*&&\s*/)) {
|
||||
const cm = cp.trim().match(/^(p|c)\s*(<|<=|>|>=)\s*([\d.eE+]+)$/);
|
||||
if (cm) conditions.push({ var: cm[1], op: cm[2], value: Number(cm[3]) });
|
||||
}
|
||||
}
|
||||
tiers.push({
|
||||
label: match[2],
|
||||
conditions,
|
||||
inputPrice: unitCostToPrice(Number(match[3])),
|
||||
outputPrice: unitCostToPrice(Number(match[4])),
|
||||
cacheReadPrice: match[5] ? unitCostToPrice(Number(match[5])) : null,
|
||||
cacheCreatePrice: match[6] ? unitCostToPrice(Number(match[6])) : null,
|
||||
cacheCreate1hPrice: match[7] ? unitCostToPrice(Number(match[7])) : null,
|
||||
});
|
||||
}
|
||||
return tiers.length > 0 ? tiers : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function describeCondition(cond, t) {
|
||||
if (cond.source === SOURCE_TIME) {
|
||||
@@ -147,7 +91,7 @@ export default function DynamicPricingBreakdown({ billingExpr, t }) {
|
||||
const { billingExpr: baseExpr, requestRuleExpr: ruleExpr } =
|
||||
splitBillingExprAndRequestRules(billingExpr || '');
|
||||
|
||||
const tiers = tryParseTiers(baseExpr);
|
||||
const tiers = parseTiersFromExpr(baseExpr);
|
||||
const ruleGroups = tryParseRequestRuleExpr(ruleExpr || '');
|
||||
|
||||
const hasTiers = tiers && tiers.length > 0;
|
||||
@@ -169,6 +113,17 @@ export default function DynamicPricingBreakdown({ billingExpr, t }) {
|
||||
);
|
||||
}
|
||||
|
||||
const priceFields = [
|
||||
['inputPrice', '输入价格'],
|
||||
['outputPrice', '补全价格'],
|
||||
['cacheReadPrice', '缓存读取'],
|
||||
['cacheCreatePrice', '缓存创建'],
|
||||
['cacheCreate1hPrice', '缓存创建-1h'],
|
||||
['imagePrice', '图片输入'],
|
||||
['audioInputPrice', '音频输入'],
|
||||
['audioOutputPrice', '音频输出'],
|
||||
];
|
||||
|
||||
const tierColumns = [
|
||||
{
|
||||
title: t('档位'),
|
||||
@@ -182,54 +137,21 @@ export default function DynamicPricingBreakdown({ billingExpr, t }) {
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: `${t('输入价格')} (${PRICE_SUFFIX})`,
|
||||
dataIndex: 'inputPrice',
|
||||
render: (v) => <Text strong>${v.toFixed(4)}</Text>,
|
||||
},
|
||||
{
|
||||
title: `${t('输出价格')} (${PRICE_SUFFIX})`,
|
||||
dataIndex: 'outputPrice',
|
||||
render: (v) => <Text strong>${v.toFixed(4)}</Text>,
|
||||
},
|
||||
...priceFields
|
||||
.filter(([field]) => hasTiers && tiers.some((tier) => tier[field] > 0))
|
||||
.map(([field, label]) => ({
|
||||
title: `${t(label)} (${PRICE_SUFFIX})`,
|
||||
dataIndex: field,
|
||||
render: (v) => v > 0 ? <Text strong>${v.toFixed(4)}</Text> : '-',
|
||||
})),
|
||||
];
|
||||
|
||||
const hasCacheRead = hasTiers && tiers.some((tier) => tier.cacheReadPrice != null);
|
||||
const hasCacheCreate = hasTiers && tiers.some((tier) => tier.cacheCreatePrice != null);
|
||||
const hasCache1h = hasTiers && tiers.some((tier) => tier.cacheCreate1hPrice != null);
|
||||
|
||||
if (hasCacheRead) {
|
||||
tierColumns.push({
|
||||
title: `${t('缓存读取')} (${PRICE_SUFFIX})`,
|
||||
dataIndex: 'cacheReadPrice',
|
||||
render: (v) => v != null ? <Text>${v.toFixed(4)}</Text> : '-',
|
||||
});
|
||||
}
|
||||
if (hasCacheCreate) {
|
||||
tierColumns.push({
|
||||
title: `${t('缓存创建')} (${PRICE_SUFFIX})`,
|
||||
dataIndex: 'cacheCreatePrice',
|
||||
render: (v) => v != null ? <Text>${v.toFixed(4)}</Text> : '-',
|
||||
});
|
||||
}
|
||||
if (hasCache1h) {
|
||||
tierColumns.push({
|
||||
title: `${t('缓存创建-1h')} (${PRICE_SUFFIX})`,
|
||||
dataIndex: 'cacheCreate1hPrice',
|
||||
render: (v) => v != null ? <Text>${v.toFixed(4)}</Text> : '-',
|
||||
});
|
||||
}
|
||||
|
||||
const tierData = hasTiers
|
||||
? tiers.map((tier, i) => ({
|
||||
key: `tier-${i}`,
|
||||
label: tier.label,
|
||||
condSummary: formatConditionSummary(tier.conditions, t),
|
||||
inputPrice: tier.inputPrice,
|
||||
outputPrice: tier.outputPrice,
|
||||
cacheReadPrice: tier.cacheReadPrice,
|
||||
cacheCreatePrice: tier.cacheCreatePrice,
|
||||
cacheCreate1hPrice: tier.cacheCreate1hPrice,
|
||||
...Object.fromEntries(priceFields.map(([field]) => [field, tier[field] || 0])),
|
||||
}))
|
||||
: [];
|
||||
|
||||
|
||||
Vendored
+71
-87
@@ -2210,24 +2210,47 @@ export function renderLogContent(opts) {
|
||||
}
|
||||
}
|
||||
|
||||
function parseTiersFromExpr(exprStr) {
|
||||
const TIER_VAR_KEYS = ['p', 'c', 'cr', 'cc', 'cc1h', 'img', 'ai', 'ao'];
|
||||
const TIER_VAR_TO_FIELD = {
|
||||
p: 'inputPrice', c: 'outputPrice',
|
||||
cr: 'cacheReadPrice', cc: 'cacheCreatePrice', cc1h: 'cacheCreate1hPrice',
|
||||
img: 'imagePrice', ai: 'audioInputPrice', ao: 'audioOutputPrice',
|
||||
};
|
||||
|
||||
function parseTierBody(bodyStr) {
|
||||
const coeffs = {};
|
||||
const re = new RegExp(`\\b(${TIER_VAR_KEYS.join('|')})\\s*\\*\\s*([\\d.eE+-]+)`, 'g');
|
||||
let m;
|
||||
while ((m = re.exec(bodyStr)) !== null) {
|
||||
if (!(m[1] in coeffs)) coeffs[m[1]] = Number(m[2]);
|
||||
}
|
||||
const tier = {};
|
||||
for (const [varName, field] of Object.entries(TIER_VAR_TO_FIELD)) {
|
||||
tier[field] = coeffs[varName] || 0;
|
||||
}
|
||||
return tier;
|
||||
}
|
||||
|
||||
export function parseTiersFromExpr(exprStr) {
|
||||
if (!exprStr) return [];
|
||||
try {
|
||||
const cacheVars = ['cr', 'cc', 'cc1h'];
|
||||
const optCache = cacheVars.map((v) => `(?:\\s*\\+\\s*${v}\\s*\\*\\s*([\\d.eE+-]+))?`).join('');
|
||||
const bodyPat = `p\\s*\\*\\s*([\\d.eE+-]+)\\s*\\+\\s*c\\s*\\*\\s*([\\d.eE+-]+)${optCache}`;
|
||||
const tierRe = new RegExp(`tier\\("([^"]*)",\\s*${bodyPat}\\)`, 'g');
|
||||
const condGroup = `((?:(?:p|c)\\s*(?:<|<=|>|>=)\\s*[\\d.eE+]+)(?:\\s*&&\\s*(?:p|c)\\s*(?:<|<=|>|>=)\\s*[\\d.eE+]+)*)`;
|
||||
const tierRe = new RegExp(`(?:${condGroup}\\s*\\?\\s*)?tier\\("([^"]*)",\\s*([^)]+)\\)`, 'g');
|
||||
const tiers = [];
|
||||
let m;
|
||||
while ((m = tierRe.exec(exprStr)) !== null) {
|
||||
tiers.push({
|
||||
label: m[1],
|
||||
inputPrice: Number(m[2]),
|
||||
outputPrice: Number(m[3]),
|
||||
cacheReadPrice: m[4] ? Number(m[4]) : 0,
|
||||
cacheCreatePrice: m[5] ? Number(m[5]) : 0,
|
||||
cacheCreate1hPrice: m[6] ? Number(m[6]) : 0,
|
||||
});
|
||||
const condStr = m[1] || '';
|
||||
const conditions = [];
|
||||
if (condStr) {
|
||||
for (const cp of condStr.split(/\s*&&\s*/)) {
|
||||
const cm = cp.trim().match(/^(p|c)\s*(<|<=|>|>=)\s*([\d.eE+]+)$/);
|
||||
if (cm) conditions.push({ var: cm[1], op: cm[2], value: Number(cm[3]) });
|
||||
}
|
||||
}
|
||||
const tier = parseTierBody(m[3]);
|
||||
tier.label = m[2];
|
||||
tier.conditions = conditions;
|
||||
tiers.push(tier);
|
||||
}
|
||||
return tiers;
|
||||
} catch {
|
||||
@@ -2258,45 +2281,24 @@ export function renderTieredModelPrice(opts) {
|
||||
const { symbol, rate } = getCurrencyConfig();
|
||||
const gr = groupRatio || 1;
|
||||
|
||||
const inputCost = (inputTokens / 1000000) * tier.inputPrice;
|
||||
const outputCost = (completionTokens / 1000000) * tier.outputPrice;
|
||||
const cacheReadCost = (cacheTokens / 1000000) * tier.cacheReadPrice;
|
||||
const hasSplitCacheCreation = cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
|
||||
let cacheCreateCost = 0;
|
||||
if (hasSplitCacheCreation) {
|
||||
cacheCreateCost = (cacheCreationTokens5m / 1000000) * tier.cacheCreatePrice
|
||||
+ (cacheCreationTokens1h / 1000000) * tier.cacheCreate1hPrice;
|
||||
} else if (cacheCreationTokens > 0) {
|
||||
cacheCreateCost = (cacheCreationTokens / 1000000) * tier.cacheCreatePrice;
|
||||
}
|
||||
const totalBeforeGroup = inputCost + outputCost + cacheReadCost + cacheCreateCost;
|
||||
const total = totalBeforeGroup * gr;
|
||||
const priceLines = [
|
||||
['inputPrice', '输入价格'],
|
||||
['outputPrice', '补全价格'],
|
||||
['cacheReadPrice', '缓存读取价格'],
|
||||
['cacheCreatePrice', '缓存创建价格'],
|
||||
['cacheCreate1hPrice', '1h缓存创建价格'],
|
||||
['imagePrice', '图片输入价格'],
|
||||
['audioInputPrice', '音频输入价格'],
|
||||
['audioOutputPrice', '音频输出价格'],
|
||||
];
|
||||
|
||||
const lines = [
|
||||
buildBillingText('命中档位:{{tier}}', { tier: matchedTier || tier.label }),
|
||||
buildBillingPriceText('输入价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.inputPrice, rate }),
|
||||
buildBillingPriceText('输出价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.outputPrice, rate }),
|
||||
cacheTokens > 0 && tier.cacheReadPrice > 0
|
||||
? buildBillingPriceText('缓存读取价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.cacheReadPrice, rate })
|
||||
: null,
|
||||
hasSplitCacheCreation && cacheCreationTokens5m > 0 && tier.cacheCreatePrice > 0
|
||||
? buildBillingPriceText('5m缓存创建价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.cacheCreatePrice, rate })
|
||||
: null,
|
||||
hasSplitCacheCreation && cacheCreationTokens1h > 0 && tier.cacheCreate1hPrice > 0
|
||||
? buildBillingPriceText('1h缓存创建价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.cacheCreate1hPrice, rate })
|
||||
: null,
|
||||
!hasSplitCacheCreation && cacheCreationTokens > 0 && tier.cacheCreatePrice > 0
|
||||
? buildBillingPriceText('缓存创建价格:{{symbol}}{{price}} / 1M tokens', { symbol, usdAmount: tier.cacheCreatePrice, rate })
|
||||
: null,
|
||||
buildBillingText(
|
||||
'(输入 {{input}} tokens / 1M tokens * {{symbol}}{{inputPrice}} + 输出 {{output}} tokens / 1M tokens * {{symbol}}{{outputPrice}}) * 分组倍率 {{ratio}} = {{symbol}}{{total}}',
|
||||
{
|
||||
input: inputTokens, output: completionTokens, symbol,
|
||||
inputPrice: formatBillingDisplayPrice(tier.inputPrice, rate),
|
||||
outputPrice: formatBillingDisplayPrice(tier.outputPrice, rate),
|
||||
ratio: gr, total: formatBillingDisplayPrice(total, rate),
|
||||
},
|
||||
),
|
||||
...priceLines
|
||||
.filter(([field]) => tier[field] > 0)
|
||||
.map(([field, label]) =>
|
||||
buildBillingPriceText(`${label}:{{symbol}}{{price}} / 1M tokens`, { symbol, usdAmount: tier[field], rate }),
|
||||
),
|
||||
];
|
||||
|
||||
return renderBillingArticle(lines);
|
||||
@@ -2329,44 +2331,26 @@ export function renderTieredModelPriceSimple(opts) {
|
||||
];
|
||||
|
||||
if (tier && isPriceDisplayMode(displayMode)) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('输入 {{price}} / 1M tokens', {
|
||||
price: formatCompactDisplayPrice(tier.inputPrice),
|
||||
}),
|
||||
});
|
||||
if (cacheTokens > 0 && tier.cacheReadPrice > 0) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('缓存读 {{price}} / 1M tokens', {
|
||||
price: formatCompactDisplayPrice(tier.cacheReadPrice),
|
||||
}),
|
||||
});
|
||||
}
|
||||
const hasSplitCacheCreation = cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
|
||||
if (hasSplitCacheCreation && cacheCreationTokens5m > 0 && tier.cacheCreatePrice > 0) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('5m缓存创建 {{price}} / 1M tokens', {
|
||||
price: formatCompactDisplayPrice(tier.cacheCreatePrice),
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (hasSplitCacheCreation && cacheCreationTokens1h > 0 && tier.cacheCreate1hPrice > 0) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('1h缓存创建 {{price}} / 1M tokens', {
|
||||
price: formatCompactDisplayPrice(tier.cacheCreate1hPrice),
|
||||
}),
|
||||
});
|
||||
}
|
||||
if (!hasSplitCacheCreation && cacheCreationTokens > 0 && tier.cacheCreatePrice > 0) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('缓存创建 {{price}} / 1M tokens', {
|
||||
price: formatCompactDisplayPrice(tier.cacheCreatePrice),
|
||||
}),
|
||||
});
|
||||
const priceSegments = [
|
||||
['inputPrice', '输入'],
|
||||
['outputPrice', '补全'],
|
||||
['cacheReadPrice', '缓存读'],
|
||||
['cacheCreatePrice', '缓存创建'],
|
||||
['cacheCreate1hPrice', '1h缓存创建'],
|
||||
['imagePrice', '图片输入'],
|
||||
['audioInputPrice', '音频输入'],
|
||||
['audioOutputPrice', '音频输出'],
|
||||
];
|
||||
for (const [field, label] of priceSegments) {
|
||||
if (tier[field] > 0) {
|
||||
segments.push({
|
||||
tone: 'secondary',
|
||||
text: i18next.t('{{label}} {{price}} / 1M tokens', {
|
||||
label: i18next.t(label),
|
||||
price: formatCompactDisplayPrice(tier[field]),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+27
-19
@@ -904,9 +904,24 @@ export const formatDynamicPriceSummary = (billingExpr, t, groupRatio = 1) => {
|
||||
const tierMatches = billingExpr.match(/tier\(/g) || [];
|
||||
const tierCount = tierMatches.length;
|
||||
|
||||
const firstTierMatch = billingExpr.match(
|
||||
/tier\("[^"]*",\s*p\s*\*\s*([\d.eE+-]+)\s*\+\s*c\s*\*\s*([\d.eE+-]+)(?:\s*\+\s*cr\s*\*\s*([\d.eE+-]+))?(?:\s*\+\s*cc\s*\*\s*([\d.eE+-]+))?/,
|
||||
);
|
||||
const varCoeffs = {};
|
||||
const varRe = /\b(p|c|cr|cc|cc1h|img|ai|ao)\s*\*\s*([\d.eE+-]+)/g;
|
||||
let vm;
|
||||
while ((vm = varRe.exec(billingExpr)) !== null) {
|
||||
if (!(vm[1] in varCoeffs)) varCoeffs[vm[1]] = Number(vm[2]);
|
||||
}
|
||||
const hasCoeffs = 'p' in varCoeffs || 'c' in varCoeffs;
|
||||
|
||||
const varLabels = [
|
||||
['p', '输入价格'],
|
||||
['c', '补全价格'],
|
||||
['cr', '缓存读取价格'],
|
||||
['cc', '缓存创建价格'],
|
||||
['cc1h', '1h缓存创建价格'],
|
||||
['img', '图片输入价格'],
|
||||
['ai', '音频输入价格'],
|
||||
['ao', '音频输出价格'],
|
||||
];
|
||||
|
||||
const hasTimeCondition = /\b(?:hour|weekday|month|day)\(/.test(billingExpr);
|
||||
const hasRequestCondition = /\b(?:param|header)\(/.test(billingExpr);
|
||||
@@ -921,26 +936,18 @@ export const formatDynamicPriceSummary = (billingExpr, t, groupRatio = 1) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{firstTierMatch && (
|
||||
{hasCoeffs && (
|
||||
<>
|
||||
<span style={lineStyle}>
|
||||
{t('输入价格')} ${(Number(firstTierMatch[1]) * gr).toFixed(4)}{unitSuffix}
|
||||
</span>
|
||||
<span style={lineStyle}>
|
||||
{t('输出价格')} ${(Number(firstTierMatch[2]) * gr).toFixed(4)}{unitSuffix}
|
||||
</span>
|
||||
{firstTierMatch[3] && (
|
||||
<span style={lineStyle}>
|
||||
{t('缓存读取价格')} ${(Number(firstTierMatch[3]) * gr).toFixed(4)}{unitSuffix}
|
||||
</span>
|
||||
)}
|
||||
{firstTierMatch[4] && (
|
||||
<span style={lineStyle}>
|
||||
{t('缓存创建价格')} ${(Number(firstTierMatch[4]) * gr).toFixed(4)}{unitSuffix}
|
||||
</span>
|
||||
{varLabels.map(([key, label]) =>
|
||||
key in varCoeffs ? (
|
||||
<span key={key} style={lineStyle}>
|
||||
{t(label)} ${(varCoeffs[key] * gr).toFixed(4)}{unitSuffix}
|
||||
</span>
|
||||
) : null,
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{(tierCount > 1 || hasTimeCondition || hasRequestCondition) && (
|
||||
<span style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
|
||||
<span
|
||||
style={{
|
||||
@@ -970,6 +977,7 @@ export const formatDynamicPriceSummary = (billingExpr, t, groupRatio = 1) => {
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -764,7 +764,7 @@ const PRESET_GROUPS = [
|
||||
presets: [
|
||||
{ key: 'flat', label: 'Flat', expr: 'tier("base", p * 2 + c * 4)' },
|
||||
{ key: 'claude-opus', label: 'Claude Opus 4.6', expr: 'tier("base", p * 5 + c * 25 + cr * 0.5 + cc * 6.25 + cc1h * 10)' },
|
||||
{ key: 'gpt-5.4', label: 'GPT-5.4', expr: 'tier("base", p * 2.5 + c * 10 + cr * 0.25)' },
|
||||
{ key: 'gpt-5.4', label: 'GPT-5.4', expr: 'tier("base", p * 2.5 + c * 15 + cr * 0.25)' },
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -778,6 +778,7 @@ const PRESET_GROUPS = [
|
||||
{
|
||||
group: '多模态',
|
||||
presets: [
|
||||
{ key: 'gpt-image-1-mini', label: 'GPT-Image-1-Mini', expr: 'tier("base", p * 2 + c * 8 + img * 2.5)' },
|
||||
{ key: 'qwen3-omni-flash', label: 'Qwen3-Omni-Flash', expr: 'tier("base", p * 0.43 + c * 3.06 + img * 0.78 + ai * 3.81 + ao * 15.11)' },
|
||||
],
|
||||
},
|
||||
@@ -791,7 +792,7 @@ const PRESET_GROUPS = [
|
||||
},
|
||||
{
|
||||
key: 'gpt-5.4-fast', label: 'GPT-5.4 Fast',
|
||||
expr: 'tier("base", p * 2.5 + c * 10 + cr * 0.25)',
|
||||
expr: 'tier("base", p * 2.5 + c * 15 + cr * 0.25)',
|
||||
requestRules: [{ conditions: [{ source: SOURCE_PARAM, path: 'service_tier', mode: MATCH_EQ, value: 'fast' }], multiplier: '2' }],
|
||||
},
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user