🚀 feat(pricing): clarify “auto” group routing chain and exclude it from price table

Detailed changes
Backend
• `controller/pricing.go` now includes `auto_groups` in `/api/pricing` response, sourced from `setting.AutoGroups`.

Frontend
• `useModelPricingData.js`
  – Parses `auto_groups` and exposes `autoGroups` state.
• `PricingPage.jsx` → `ModelDetailSideSheet.jsx` → `ModelPricingTable.jsx`
  – Thread `autoGroups` through component tree.
• `ModelPricingTable.jsx`
  – Removes deprecated `getGroupDescription` / `Tooltip`.
  – Filters out `auto` when building price table rows.
  – Renders a descriptive banner: “auto 分组调用链路 → auto → group1 → …”, clarifying fallback order without showing prices.
• Minor i18n tweak: adds `auto分组调用链路` key for the banner text.

Why
Users were confused by the “auto” tag appearing alongside regular groups with no price.
This change:
1. Makes the routing chain explicit.
2. Keeps the pricing table focused on billable groups.

No breaking API changes; existing clients can ignore the new `auto_groups` field.
This commit is contained in:
t0ng7u
2025-08-08 14:49:55 +08:00
parent 29ec328f46
commit 1690b05629
5 changed files with 35 additions and 32 deletions

View File

@@ -45,6 +45,7 @@ func GetPricing(c *gin.Context) {
"group_ratio": groupRatio, "group_ratio": groupRatio,
"usable_group": usableGroup, "usable_group": usableGroup,
"supported_endpoint": model.GetSupportedEndpointMap(), "supported_endpoint": model.GetSupportedEndpointMap(),
"auto_groups": setting.AutoGroups,
}) })
} }

View File

@@ -72,7 +72,6 @@ const PricingPage = () => {
visible={pricingData.showModelDetail} visible={pricingData.showModelDetail}
onClose={pricingData.closeModelDetail} onClose={pricingData.closeModelDetail}
modelData={pricingData.selectedModel} modelData={pricingData.selectedModel}
selectedGroup={pricingData.selectedGroup}
groupRatio={pricingData.groupRatio} groupRatio={pricingData.groupRatio}
usableGroup={pricingData.usableGroup} usableGroup={pricingData.usableGroup}
currency={pricingData.currency} currency={pricingData.currency}
@@ -81,6 +80,7 @@ const PricingPage = () => {
showRatio={allProps.showRatio} showRatio={allProps.showRatio}
vendorsMap={pricingData.vendorsMap} vendorsMap={pricingData.vendorsMap}
endpointMap={pricingData.endpointMap} endpointMap={pricingData.endpointMap}
autoGroups={pricingData.autoGroups}
t={pricingData.t} t={pricingData.t}
/> />
</div> </div>

View File

@@ -39,7 +39,6 @@ const ModelDetailSideSheet = ({
visible, visible,
onClose, onClose,
modelData, modelData,
selectedGroup,
groupRatio, groupRatio,
currency, currency,
tokenUnit, tokenUnit,
@@ -48,6 +47,7 @@ const ModelDetailSideSheet = ({
usableGroup, usableGroup,
vendorsMap, vendorsMap,
endpointMap, endpointMap,
autoGroups,
t, t,
}) => { }) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
@@ -86,13 +86,13 @@ const ModelDetailSideSheet = ({
<ModelEndpoints modelData={modelData} endpointMap={endpointMap} t={t} /> <ModelEndpoints modelData={modelData} endpointMap={endpointMap} t={t} />
<ModelPricingTable <ModelPricingTable
modelData={modelData} modelData={modelData}
selectedGroup={selectedGroup}
groupRatio={groupRatio} groupRatio={groupRatio}
currency={currency} currency={currency}
tokenUnit={tokenUnit} tokenUnit={tokenUnit}
displayPrice={displayPrice} displayPrice={displayPrice}
showRatio={showRatio} showRatio={showRatio}
usableGroup={usableGroup} usableGroup={usableGroup}
autoGroups={autoGroups}
t={t} t={t}
/> />
</> </>

View File

@@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com
*/ */
import React from 'react'; import React from 'react';
import { Card, Avatar, Typography, Table, Tag, Tooltip } from '@douyinfe/semi-ui'; import { Card, Avatar, Typography, Table, Tag } from '@douyinfe/semi-ui';
import { IconCoinMoneyStroked } from '@douyinfe/semi-icons'; import { IconCoinMoneyStroked } from '@douyinfe/semi-icons';
import { calculateModelPrice } from '../../../../../helpers'; import { calculateModelPrice } from '../../../../../helpers';
@@ -26,34 +26,24 @@ const { Text } = Typography;
const ModelPricingTable = ({ const ModelPricingTable = ({
modelData, modelData,
selectedGroup,
groupRatio, groupRatio,
currency, currency,
tokenUnit, tokenUnit,
displayPrice, displayPrice,
showRatio, showRatio,
usableGroup, usableGroup,
autoGroups = [],
t, t,
}) => { }) => {
// 获取分组介绍
const getGroupDescription = (groupName) => {
const descriptions = {
'default': t('默认分组,适用于普通用户'),
'ssvip': t('超级VIP分组享受最优惠价格'),
'openai官-优质': t('OpenAI官方优质分组最快最稳支持o1、realtime等'),
'origin': t('企业分组OpenAI&Claude官方原价不升价本分组稳定性可用性'),
'vip': t('VIP分组享受优惠价格'),
'premium': t('高级分组,稳定可靠'),
'enterprise': t('企业级分组,专业服务'),
};
return descriptions[groupName] || t('用户分组');
};
const renderGroupPriceTable = () => { const renderGroupPriceTable = () => {
const availableGroups = Object.keys(usableGroup || {}).filter(g => g !== ''); // 仅展示模型可用的分组:模型 enable_groups 与用户可用分组的交集
if (availableGroups.length === 0) { const modelEnableGroups = Array.isArray(modelData?.enable_groups)
availableGroups.push('default'); ? modelData.enable_groups
} : [];
const availableGroups = Object.keys(usableGroup || {})
.filter(g => g !== '')
.filter(g => g !== 'auto')
.filter(g => modelEnableGroups.includes(g));
// 准备表格数据 // 准备表格数据
const tableData = availableGroups.map(group => { const tableData = availableGroups.map(group => {
@@ -72,7 +62,6 @@ const ModelPricingTable = ({
return { return {
key: group, key: group,
group: group, group: group,
description: getGroupDescription(group),
ratio: groupRatioValue, ratio: groupRatioValue,
billingType: modelData?.quota_type === 0 ? t('按量计费') : t('按次计费'), billingType: modelData?.quota_type === 0 ? t('按量计费') : t('按次计费'),
inputPrice: modelData?.quota_type === 0 ? priceData.inputPrice : '-', inputPrice: modelData?.quota_type === 0 ? priceData.inputPrice : '-',
@@ -86,12 +75,10 @@ const ModelPricingTable = ({
{ {
title: t('分组'), title: t('分组'),
dataIndex: 'group', dataIndex: 'group',
render: (text, record) => ( render: (text) => (
<Tooltip content={record.description} position="top"> <Tag color="white" size="small" shape="circle">
<Tag color="white" size="small" shape="circle" className="cursor-help">
{text}{t('分组')} {text}{t('分组')}
</Tag> </Tag>
</Tooltip>
), ),
}, },
]; ];
@@ -182,6 +169,18 @@ const ModelPricingTable = ({
<div className="text-xs text-gray-600">{t('不同用户分组的价格信息')}</div> <div className="text-xs text-gray-600">{t('不同用户分组的价格信息')}</div>
</div> </div>
</div> </div>
{autoGroups && autoGroups.length > 0 && (
<div className="flex flex-wrap items-center gap-1 mb-4">
<span className="text-sm text-gray-600">{t('auto分组调用链路')}</span>
<span className="text-sm"></span>
{autoGroups.map((g, idx) => (
<React.Fragment key={g}>
<Tag color="white" size="small" shape="circle">{g}{t('分组')}</Tag>
{idx < autoGroups.length - 1 && <span className="text-sm"></span>}
</React.Fragment>
))}
</div>
)}
{renderGroupPriceTable()} {renderGroupPriceTable()}
</Card> </Card>
); );

View File

@@ -49,6 +49,7 @@ export const useModelPricingData = () => {
const [groupRatio, setGroupRatio] = useState({}); const [groupRatio, setGroupRatio] = useState({});
const [usableGroup, setUsableGroup] = useState({}); const [usableGroup, setUsableGroup] = useState({});
const [endpointMap, setEndpointMap] = useState({}); const [endpointMap, setEndpointMap] = useState({});
const [autoGroups, setAutoGroups] = useState([]);
const [statusState] = useContext(StatusContext); const [statusState] = useContext(StatusContext);
const [userState] = useContext(UserContext); const [userState] = useContext(UserContext);
@@ -160,7 +161,7 @@ export const useModelPricingData = () => {
setLoading(true); setLoading(true);
let url = '/api/pricing'; let url = '/api/pricing';
const res = await API.get(url); const res = await API.get(url);
const { success, message, data, vendors, group_ratio, usable_group, supported_endpoint } = res.data; const { success, message, data, vendors, group_ratio, usable_group, supported_endpoint, auto_groups } = res.data;
if (success) { if (success) {
setGroupRatio(group_ratio); setGroupRatio(group_ratio);
setUsableGroup(usable_group); setUsableGroup(usable_group);
@@ -174,6 +175,7 @@ export const useModelPricingData = () => {
} }
setVendorsMap(vendorMap); setVendorsMap(vendorMap);
setEndpointMap(supported_endpoint || {}); setEndpointMap(supported_endpoint || {});
setAutoGroups(auto_groups || []);
setModelsFormat(data, group_ratio, vendorMap); setModelsFormat(data, group_ratio, vendorMap);
} else { } else {
showError(message); showError(message);
@@ -282,6 +284,7 @@ export const useModelPricingData = () => {
groupRatio, groupRatio,
usableGroup, usableGroup,
endpointMap, endpointMap,
autoGroups,
// 计算属性 // 计算属性
priceRate, priceRate,