diff --git a/scripts/deploy.sh b/scripts/deploy.sh index 7065bcd..8abc6bb 100644 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -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 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 266bbf9..6f03729 100644 --- a/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx +++ b/web/src/components/table/model-pricing/modal/components/ModelPricingTable.jsx @@ -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) => ( + <> +
{text}
+
+ / {tokenUnit === 'K' ? '1K' : '1M'} tokens +
+ + ), + }); + } + // 缓存创建价格列(仅在有缓存价格数据时显示,与缓存读取列同时出现) + if (hasCachePricing) { + columns.push({ + title: t('缓存创建'), + dataIndex: 'cacheCreationPrice', + render: (text) => ( + <> +
{text}
+
+ / {tokenUnit === 'K' ? '1K' : '1M'} tokens +
+ + ), + }); + } } else { // 按次计费 columns.push({ diff --git a/web/src/helpers/utils.jsx b/web/src/helpers/utils.jsx index 4b78335..a2aeadc 100644 --- a/web/src/helpers/utils.jsx +++ b/web/src/helpers/utils.jsx @@ -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,