From 1690b056298d4a16a756336345964c1b96682719 Mon Sep 17 00:00:00 2001 From: t0ng7u Date: Fri, 8 Aug 2025 14:49:55 +0800 Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=80=20feat(pricing):=20clarify=20?= =?UTF-8?q?=E2=80=9Cauto=E2=80=9D=20group=20routing=20chain=20and=20exclud?= =?UTF-8?q?e=20it=20from=20price=20table?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- controller/pricing.go | 3 +- .../model-pricing/layout/PricingPage.jsx | 2 +- .../modal/ModelDetailSideSheet.jsx | 4 +- .../modal/components/ModelPricingTable.jsx | 53 +++++++++---------- .../model-pricing/useModelPricingData.js | 5 +- 5 files changed, 35 insertions(+), 32 deletions(-) diff --git a/controller/pricing.go b/controller/pricing.go index e1719cf3..898c9f9f 100644 --- a/controller/pricing.go +++ b/controller/pricing.go @@ -42,9 +42,10 @@ func GetPricing(c *gin.Context) { "success": true, "data": pricing, "vendors": model.GetVendors(), - "group_ratio": groupRatio, + "group_ratio": groupRatio, "usable_group": usableGroup, "supported_endpoint": model.GetSupportedEndpointMap(), + "auto_groups": setting.AutoGroups, }) } diff --git a/web/src/components/table/model-pricing/layout/PricingPage.jsx b/web/src/components/table/model-pricing/layout/PricingPage.jsx index 69ac2336..a2c34435 100644 --- a/web/src/components/table/model-pricing/layout/PricingPage.jsx +++ b/web/src/components/table/model-pricing/layout/PricingPage.jsx @@ -72,7 +72,6 @@ const PricingPage = () => { visible={pricingData.showModelDetail} onClose={pricingData.closeModelDetail} modelData={pricingData.selectedModel} - selectedGroup={pricingData.selectedGroup} groupRatio={pricingData.groupRatio} usableGroup={pricingData.usableGroup} currency={pricingData.currency} @@ -81,6 +80,7 @@ const PricingPage = () => { showRatio={allProps.showRatio} vendorsMap={pricingData.vendorsMap} endpointMap={pricingData.endpointMap} + autoGroups={pricingData.autoGroups} t={pricingData.t} /> diff --git a/web/src/components/table/model-pricing/modal/ModelDetailSideSheet.jsx b/web/src/components/table/model-pricing/modal/ModelDetailSideSheet.jsx index 44a3607c..66a1bfe6 100644 --- a/web/src/components/table/model-pricing/modal/ModelDetailSideSheet.jsx +++ b/web/src/components/table/model-pricing/modal/ModelDetailSideSheet.jsx @@ -39,7 +39,6 @@ const ModelDetailSideSheet = ({ visible, onClose, modelData, - selectedGroup, groupRatio, currency, tokenUnit, @@ -48,6 +47,7 @@ const ModelDetailSideSheet = ({ usableGroup, vendorsMap, endpointMap, + autoGroups, t, }) => { const isMobile = useIsMobile(); @@ -86,13 +86,13 @@ const ModelDetailSideSheet = ({ diff --git a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx index 3d8d84be..de52eae6 100644 --- a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx +++ b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx @@ -18,7 +18,7 @@ For commercial licensing, please contact support@quantumnous.com */ 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 { calculateModelPrice } from '../../../../../helpers'; @@ -26,34 +26,24 @@ const { Text } = Typography; const ModelPricingTable = ({ modelData, - selectedGroup, groupRatio, currency, tokenUnit, displayPrice, showRatio, usableGroup, + autoGroups = [], 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 availableGroups = Object.keys(usableGroup || {}).filter(g => g !== ''); - if (availableGroups.length === 0) { - availableGroups.push('default'); - } + // 仅展示模型可用的分组:模型 enable_groups 与用户可用分组的交集 + const modelEnableGroups = Array.isArray(modelData?.enable_groups) + ? 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 => { @@ -72,7 +62,6 @@ const ModelPricingTable = ({ return { key: group, group: group, - description: getGroupDescription(group), ratio: groupRatioValue, billingType: modelData?.quota_type === 0 ? t('按量计费') : t('按次计费'), inputPrice: modelData?.quota_type === 0 ? priceData.inputPrice : '-', @@ -86,12 +75,10 @@ const ModelPricingTable = ({ { title: t('分组'), dataIndex: 'group', - render: (text, record) => ( - - - {text}{t('分组')} - - + render: (text) => ( + + {text}{t('分组')} + ), }, ]; @@ -182,6 +169,18 @@ const ModelPricingTable = ({
{t('不同用户分组的价格信息')}
+ {autoGroups && autoGroups.length > 0 && ( +
+ {t('auto分组调用链路')} + + {autoGroups.map((g, idx) => ( + + {g}{t('分组')} + {idx < autoGroups.length - 1 && } + + ))} +
+ )} {renderGroupPriceTable()} ); diff --git a/web/src/hooks/model-pricing/useModelPricingData.js b/web/src/hooks/model-pricing/useModelPricingData.js index 966e346b..71c29a41 100644 --- a/web/src/hooks/model-pricing/useModelPricingData.js +++ b/web/src/hooks/model-pricing/useModelPricingData.js @@ -49,6 +49,7 @@ export const useModelPricingData = () => { const [groupRatio, setGroupRatio] = useState({}); const [usableGroup, setUsableGroup] = useState({}); const [endpointMap, setEndpointMap] = useState({}); + const [autoGroups, setAutoGroups] = useState([]); const [statusState] = useContext(StatusContext); const [userState] = useContext(UserContext); @@ -160,7 +161,7 @@ export const useModelPricingData = () => { setLoading(true); let url = '/api/pricing'; 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) { setGroupRatio(group_ratio); setUsableGroup(usable_group); @@ -174,6 +175,7 @@ export const useModelPricingData = () => { } setVendorsMap(vendorMap); setEndpointMap(supported_endpoint || {}); + setAutoGroups(auto_groups || []); setModelsFormat(data, group_ratio, vendorMap); } else { showError(message); @@ -282,6 +284,7 @@ export const useModelPricingData = () => { groupRatio, usableGroup, endpointMap, + autoGroups, // 计算属性 priceRate,