feat: implement tiered billing expression evaluation and related functionality

- Added support for tiered billing expressions in the billing system.
- Introduced new types and functions for handling billing expressions, including caching and execution.
- Updated existing billing logic to accommodate tiered billing scenarios.
- Enhanced request handling to support incoming billing expression requests.
- Added tests for tiered billing functionality to ensure correctness.
This commit is contained in:
CaIon
2026-03-17 16:59:25 +08:00
parent a4fd2246ba
commit 91ed4e196a
34 changed files with 4797 additions and 26 deletions
@@ -17,7 +17,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import React, { useMemo, useState } from 'react';
import React, { useCallback, useMemo, useState } from 'react';
import {
Banner,
Button,
@@ -49,6 +49,7 @@ import {
useModelPricingEditorState,
} from '../hooks/useModelPricingEditorState';
import { useIsMobile } from '../../../../hooks/common/useIsMobile';
import TieredPricingEditor from './TieredPricingEditor';
const { Text } = Typography;
const EMPTY_CANDIDATE_MODEL_NAMES = [];
@@ -123,6 +124,8 @@ export default function ModelPricingEditor({
handleOptionalFieldToggle,
handleNumericFieldChange,
handleBillingModeChange,
handleBillingExprChange,
handleRequestRuleExprChange,
handleSubmit,
addModel,
deleteModel,
@@ -135,6 +138,15 @@ export default function ModelPricingEditor({
filterMode,
});
const getExprModeLabel = useCallback((model) => {
if (model?.billingMode !== 'tiered_expr') {
return '';
}
return (model.billingExpr || '').includes('tier(')
? t('阶梯计费')
: t('表达式计费');
}, [t]);
const columns = useMemo(
() => [
{
@@ -175,10 +187,20 @@ export default function ModelPricingEditor({
dataIndex: 'billingMode',
key: 'billingMode',
render: (_, record) => (
<Tag color={record.billingMode === 'per-request' ? 'teal' : 'violet'}>
<Tag
color={
record.billingMode === 'per-request'
? 'teal'
: record.billingMode === 'tiered_expr'
? 'amber'
: 'violet'
}
>
{record.billingMode === 'per-request'
? t('按次计费')
: t('按量计费')}
: record.billingMode === 'tiered_expr'
? getExprModeLabel(record)
: t('按量计费')}
</Tag>
),
},
@@ -208,6 +230,7 @@ export default function ModelPricingEditor({
[
allowDeleteModel,
deleteModel,
getExprModeLabel,
selectedModelName,
selectedModelNames,
setSelectedModelName,
@@ -353,10 +376,20 @@ export default function ModelPricingEditor({
title={selectedModel ? selectedModel.name : t('模型计费编辑器')}
headerExtraContent={
selectedModel ? (
<Tag color='blue'>
<Tag
color={
selectedModel.billingMode === 'per-request'
? 'teal'
: selectedModel.billingMode === 'tiered_expr'
? 'amber'
: 'blue'
}
>
{selectedModel.billingMode === 'per-request'
? t('按次计费')
: t('按量计费')}
: selectedModel.billingMode === 'tiered_expr'
? getExprModeLabel(selectedModel)
: t('按量计费')}
</Tag>
) : null
}
@@ -381,10 +414,11 @@ export default function ModelPricingEditor({
>
<Radio value='per-token'>{t('按量计费')}</Radio>
<Radio value='per-request'>{t('按次计费')}</Radio>
<Radio value='tiered_expr'>{t('表达式/阶梯计费')}</Radio>
</RadioGroup>
<div className='mt-2 text-xs text-gray-500'>
{t(
'这个界面默认按价格填写,保存时会自动换算回后端需要的倍率 JSON。',
'普通按量/按次直接填价格就行;如果价格要跟请求参数或请求头联动,请切到表达式/阶梯计费。',
)}
</div>
</div>
@@ -415,6 +449,14 @@ export default function ModelPricingEditor({
onChange={(value) => handleNumericFieldChange('fixedPrice', value)}
extraText={t('适合 MJ / 任务类等按次收费模型。')}
/>
) : selectedModel.billingMode === 'tiered_expr' ? (
<TieredPricingEditor
model={selectedModel}
onExprChange={handleBillingExprChange}
requestRuleExpr={selectedModel.requestRuleExpr}
onRequestRuleExprChange={handleRequestRuleExprChange}
t={t}
/>
) : (
<>
<Card