- calculateModelPrice 新增缓存读取/创建价格计算逻辑 - ModelPricingTable 条件显示缓存价格列(仅 cache_ratio !== 1 时) - deploy.sh 添加镜像拉取超时(120s)和3次重试机制master
| @@ -127,7 +127,18 @@ echo " [1/4] 更新 image tag..." | |||
| sed -i "s|${REGISTRY}:.*|${REGISTRY}:${TAG}|" docker-compose.yml | |||
| echo " [2/4] 拉取新镜像..." | |||
| docker compose pull new-api | |||
| PULL_TIMEOUT=120 | |||
| for i in 1 2 3; do | |||
| if timeout "${PULL_TIMEOUT}" docker compose pull --quiet new-api; then | |||
| break | |||
| fi | |||
| if [ "$i" -eq 3 ]; then | |||
| echo " [2/4] 拉取失败,已重试 3 次(每次 ${PULL_TIMEOUT}s 超时)" | |||
| exit 1 | |||
| fi | |||
| echo " [2/4] 拉取超时或失败(${PULL_TIMEOUT}s),第 $((i+1)) 次重试..." | |||
| sleep 5 | |||
| done | |||
| echo " [3/4] 重启服务..." | |||
| docker compose up -d new-api | |||
| @@ -79,6 +79,14 @@ const ModelPricingTable = ({ | |||
| modelData?.quota_type === 0 | |||
| ? priceData.completionPrice || priceData.outputPrice | |||
| : '-', | |||
| cachePrice: | |||
| modelData?.quota_type === 0 && priceData.cacheRatio !== 1 | |||
| ? priceData.cachePrice | |||
| : '-', | |||
| cacheCreationPrice: | |||
| modelData?.quota_type === 0 && priceData.cacheRatio !== 1 | |||
| ? priceData.cacheCreationPrice | |||
| : '-', | |||
| fixedPrice: modelData?.quota_type === 1 ? priceData.price : '-', | |||
| }; | |||
| }); | |||
| @@ -126,6 +134,9 @@ const ModelPricingTable = ({ | |||
| }, | |||
| }); | |||
| // 检查是否有缓存价格数据(模型支持缓存时同时显示缓存读取和缓存创建) | |||
| const hasCachePricing = tableData.some((row) => row.cachePrice !== '-'); | |||
| // 根据计费类型添加价格列 | |||
| if (modelData?.quota_type === 0) { | |||
| // 按量计费 | |||
| @@ -155,6 +166,36 @@ const ModelPricingTable = ({ | |||
| ), | |||
| }, | |||
| ); | |||
| // 缓存读取价格列(仅在有缓存倍率时显示) | |||
| if (hasCachePricing) { | |||
| columns.push({ | |||
| title: t('缓存读取'), | |||
| dataIndex: 'cachePrice', | |||
| render: (text) => ( | |||
| <> | |||
| <div className='font-semibold text-orange-600'>{text}</div> | |||
| <div className='text-xs text-gray-500'> | |||
| / {tokenUnit === 'K' ? '1K' : '1M'} tokens | |||
| </div> | |||
| </> | |||
| ), | |||
| }); | |||
| } | |||
| // 缓存创建价格列(仅在有缓存价格数据时显示,与缓存读取列同时出现) | |||
| if (hasCachePricing) { | |||
| columns.push({ | |||
| title: t('缓存创建'), | |||
| dataIndex: 'cacheCreationPrice', | |||
| render: (text) => ( | |||
| <> | |||
| <div className='font-semibold text-orange-600'>{text}</div> | |||
| <div className='text-xs text-gray-500'> | |||
| / {tokenUnit === 'K' ? '1K' : '1M'} tokens | |||
| </div> | |||
| </> | |||
| ), | |||
| }); | |||
| } | |||
| } else { | |||
| // 按次计费 | |||
| columns.push({ | |||
| @@ -652,16 +652,29 @@ export const calculateModelPrice = ({ | |||
| const completionRatioPriceUSD = | |||
| record.model_ratio * record.completion_ratio * 2 * usedGroupRatio; | |||
| // 缓存读取价格 = 输入价格 × 缓存倍率(默认 1 表示与输入价格相同) | |||
| const cacheRatio = record.cache_ratio ?? 1; | |||
| const cacheRatioPriceUSD = inputRatioPriceUSD * cacheRatio; | |||
| // 缓存创建价格 = 输入价格 × 缓存创建倍率(默认 1.25) | |||
| const cacheCreationRatio = record.cache_creation_ratio ?? 1.25; | |||
| const cacheCreationRatioPriceUSD = inputRatioPriceUSD * cacheCreationRatio; | |||
| const unitDivisor = tokenUnit === 'K' ? 1000 : 1; | |||
| const unitLabel = tokenUnit === 'K' ? 'K' : 'M'; | |||
| const rawDisplayInput = displayPrice(inputRatioPriceUSD); | |||
| const rawDisplayCompletion = displayPrice(completionRatioPriceUSD); | |||
| const rawDisplayCache = displayPrice(cacheRatioPriceUSD); | |||
| const rawDisplayCacheCreation = displayPrice(cacheCreationRatioPriceUSD); | |||
| const numInput = | |||
| parseFloat(rawDisplayInput.replace(/[^0-9.]/g, '')) / unitDivisor; | |||
| const numCompletion = | |||
| parseFloat(rawDisplayCompletion.replace(/[^0-9.]/g, '')) / unitDivisor; | |||
| const numCache = | |||
| parseFloat(rawDisplayCache.replace(/[^0-9.]/g, '')) / unitDivisor; | |||
| const numCacheCreation = | |||
| parseFloat(rawDisplayCacheCreation.replace(/[^0-9.]/g, '')) / unitDivisor; | |||
| let symbol = '$'; | |||
| if (currency === 'CNY') { | |||
| @@ -683,6 +696,10 @@ export const calculateModelPrice = ({ | |||
| return { | |||
| inputPrice: `${symbol}${numInput.toFixed(precision)}`, | |||
| completionPrice: `${symbol}${numCompletion.toFixed(precision)}`, | |||
| cachePrice: `${symbol}${numCache.toFixed(precision)}`, | |||
| cacheCreationPrice: `${symbol}${numCacheCreation.toFixed(precision)}`, | |||
| cacheRatio, | |||
| cacheCreationRatio, | |||
| unitLabel, | |||
| isPerToken: true, | |||
| usedGroup, | |||