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,