From df2ee649ab43a86e15a10abcd5c3fefaee240706 Mon Sep 17 00:00:00 2001
From: Seefs <40468931+seefs001@users.noreply.github.com>
Date: Tue, 4 Nov 2025 00:20:50 +0800
Subject: [PATCH] feat: claude 1h cache (#2155)
* feat: claude 1h cache
* feat: claude 1h cache
* fix price
---
dto/claude.go | 43 ++-
dto/openai_response.go | 5 +
relay/channel/claude/relay-claude.go | 4 +
relay/helper/price.go | 10 +
service/log_info_generate.go | 14 +-
service/quota.go | 17 +-
types/price_data.go | 4 +-
.../table/usage-logs/UsageLogsColumnDefs.jsx | 8 +
web/src/helpers/render.jsx | 311 +++++++++++++++---
web/src/hooks/usage-logs/useUsageLogsData.jsx | 8 +
web/src/i18n/locales/en.json | 6 +-
web/src/i18n/locales/fr.json | 4 +
web/src/i18n/locales/ja.json | 6 +-
web/src/i18n/locales/ru.json | 4 +
web/src/i18n/locales/zh.json | 6 +-
15 files changed, 384 insertions(+), 66 deletions(-)
diff --git a/dto/claude.go b/dto/claude.go
index c6e5c3ecd..3646fa815 100644
--- a/dto/claude.go
+++ b/dto/claude.go
@@ -510,11 +510,44 @@ func (c *ClaudeResponse) GetClaudeError() *types.ClaudeError {
}
type ClaudeUsage struct {
- InputTokens int `json:"input_tokens"`
- CacheCreationInputTokens int `json:"cache_creation_input_tokens"`
- CacheReadInputTokens int `json:"cache_read_input_tokens"`
- OutputTokens int `json:"output_tokens"`
- ServerToolUse *ClaudeServerToolUse `json:"server_tool_use,omitempty"`
+ InputTokens int `json:"input_tokens"`
+ CacheCreationInputTokens int `json:"cache_creation_input_tokens"`
+ CacheReadInputTokens int `json:"cache_read_input_tokens"`
+ OutputTokens int `json:"output_tokens"`
+ CacheCreation *ClaudeCacheCreationUsage `json:"cache_creation,omitempty"`
+ // claude cache 1h
+ ClaudeCacheCreation5mTokens int `json:"claude_cache_creation_5_m_tokens"`
+ ClaudeCacheCreation1hTokens int `json:"claude_cache_creation_1_h_tokens"`
+ ServerToolUse *ClaudeServerToolUse `json:"server_tool_use,omitempty"`
+}
+
+type ClaudeCacheCreationUsage struct {
+ Ephemeral5mInputTokens int `json:"ephemeral_5m_input_tokens,omitempty"`
+ Ephemeral1hInputTokens int `json:"ephemeral_1h_input_tokens,omitempty"`
+}
+
+func (u *ClaudeUsage) GetCacheCreation5mTokens() int {
+ if u == nil || u.CacheCreation == nil {
+ return 0
+ }
+ return u.CacheCreation.Ephemeral5mInputTokens
+}
+
+func (u *ClaudeUsage) GetCacheCreation1hTokens() int {
+ if u == nil || u.CacheCreation == nil {
+ return 0
+ }
+ return u.CacheCreation.Ephemeral1hInputTokens
+}
+
+func (u *ClaudeUsage) GetCacheCreationTotalTokens() int {
+ if u == nil {
+ return 0
+ }
+ if u.CacheCreationInputTokens > 0 {
+ return u.CacheCreationInputTokens
+ }
+ return u.GetCacheCreation5mTokens() + u.GetCacheCreation1hTokens()
}
type ClaudeServerToolUse struct {
diff --git a/dto/openai_response.go b/dto/openai_response.go
index 0c345bacb..6baee78cc 100644
--- a/dto/openai_response.go
+++ b/dto/openai_response.go
@@ -230,6 +230,11 @@ type Usage struct {
InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"`
InputTokensDetails *InputTokenDetails `json:"input_tokens_details"`
+
+ // claude cache 1h
+ ClaudeCacheCreation5mTokens int `json:"claude_cache_creation_5_m_tokens"`
+ ClaudeCacheCreation1hTokens int `json:"claude_cache_creation_1_h_tokens"`
+
// OpenRouter Params
Cost any `json:"cost,omitempty"`
}
diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go
index fe523deff..9c72270f6 100644
--- a/relay/channel/claude/relay-claude.go
+++ b/relay/channel/claude/relay-claude.go
@@ -596,6 +596,8 @@ func FormatClaudeResponseInfo(requestMode int, claudeResponse *dto.ClaudeRespons
claudeInfo.Usage.PromptTokens = claudeResponse.Message.Usage.InputTokens
claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Message.Usage.CacheReadInputTokens
claudeInfo.Usage.PromptTokensDetails.CachedCreationTokens = claudeResponse.Message.Usage.CacheCreationInputTokens
+ claudeInfo.Usage.ClaudeCacheCreation5mTokens = claudeResponse.Message.Usage.GetCacheCreation5mTokens()
+ claudeInfo.Usage.ClaudeCacheCreation1hTokens = claudeResponse.Message.Usage.GetCacheCreation1hTokens()
claudeInfo.Usage.CompletionTokens = claudeResponse.Message.Usage.OutputTokens
} else if claudeResponse.Type == "content_block_delta" {
if claudeResponse.Delta.Text != nil {
@@ -740,6 +742,8 @@ func HandleClaudeResponseData(c *gin.Context, info *relaycommon.RelayInfo, claud
claudeInfo.Usage.TotalTokens = claudeResponse.Usage.InputTokens + claudeResponse.Usage.OutputTokens
claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Usage.CacheReadInputTokens
claudeInfo.Usage.PromptTokensDetails.CachedCreationTokens = claudeResponse.Usage.CacheCreationInputTokens
+ claudeInfo.Usage.ClaudeCacheCreation5mTokens = claudeResponse.Usage.GetCacheCreation5mTokens()
+ claudeInfo.Usage.ClaudeCacheCreation1hTokens = claudeResponse.Usage.GetCacheCreation1hTokens()
}
var responseData []byte
switch info.RelayFormat {
diff --git a/relay/helper/price.go b/relay/helper/price.go
index 8752b241d..2a774e405 100644
--- a/relay/helper/price.go
+++ b/relay/helper/price.go
@@ -13,6 +13,9 @@ import (
"github.com/gin-gonic/gin"
)
+// https://docs.claude.com/en/docs/build-with-claude/prompt-caching#1-hour-cache-duration
+const claudeCacheCreation1hMultiplier = 6 / 3.75
+
// HandleGroupRatio checks for "auto_group" in the context and updates the group ratio and relayInfo.UsingGroup if present
func HandleGroupRatio(ctx *gin.Context, relayInfo *relaycommon.RelayInfo) types.GroupRatioInfo {
groupRatioInfo := types.GroupRatioInfo{
@@ -53,6 +56,8 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
var cacheRatio float64
var imageRatio float64
var cacheCreationRatio float64
+ var cacheCreationRatio5m float64
+ var cacheCreationRatio1h float64
var audioRatio float64
var audioCompletionRatio float64
var freeModel bool
@@ -76,6 +81,9 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName)
cacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName)
cacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName)
+ cacheCreationRatio5m = cacheCreationRatio
+ // 固定1h和5min缓存写入价格的比例
+ cacheCreationRatio1h = cacheCreationRatio * claudeCacheCreation1hMultiplier
imageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName)
audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName)
audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
@@ -116,6 +124,8 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
AudioRatio: audioRatio,
AudioCompletionRatio: audioCompletionRatio,
CacheCreationRatio: cacheCreationRatio,
+ CacheCreation5mRatio: cacheCreationRatio5m,
+ CacheCreation1hRatio: cacheCreationRatio1h,
QuotaToPreConsume: preConsumedQuota,
}
diff --git a/service/log_info_generate.go b/service/log_info_generate.go
index 95a88dfbc..40121b0cc 100644
--- a/service/log_info_generate.go
+++ b/service/log_info_generate.go
@@ -92,11 +92,23 @@ func GenerateAudioOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
}
func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64,
- cacheTokens int, cacheRatio float64, cacheCreationTokens int, cacheCreationRatio float64, modelPrice float64, userGroupRatio float64) map[string]interface{} {
+ cacheTokens int, cacheRatio float64,
+ cacheCreationTokens int, cacheCreationRatio float64,
+ cacheCreationTokens5m int, cacheCreationRatio5m float64,
+ cacheCreationTokens1h int, cacheCreationRatio1h float64,
+ modelPrice float64, userGroupRatio float64) map[string]interface{} {
info := GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, cacheTokens, cacheRatio, modelPrice, userGroupRatio)
info["claude"] = true
info["cache_creation_tokens"] = cacheCreationTokens
info["cache_creation_ratio"] = cacheCreationRatio
+ if cacheCreationTokens5m != 0 {
+ info["cache_creation_tokens_5m"] = cacheCreationTokens5m
+ info["cache_creation_ratio_5m"] = cacheCreationRatio5m
+ }
+ if cacheCreationTokens1h != 0 {
+ info["cache_creation_tokens_1h"] = cacheCreationTokens1h
+ info["cache_creation_ratio_1h"] = cacheCreationRatio1h
+ }
return info
}
diff --git a/service/quota.go b/service/quota.go
index c34a36dad..0f41b851b 100644
--- a/service/quota.go
+++ b/service/quota.go
@@ -251,7 +251,11 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
cacheTokens := usage.PromptTokensDetails.CachedTokens
cacheCreationRatio := relayInfo.PriceData.CacheCreationRatio
+ cacheCreationRatio5m := relayInfo.PriceData.CacheCreation5mRatio
+ cacheCreationRatio1h := relayInfo.PriceData.CacheCreation1hRatio
cacheCreationTokens := usage.PromptTokensDetails.CachedCreationTokens
+ cacheCreationTokens5m := usage.ClaudeCacheCreation5mTokens
+ cacheCreationTokens1h := usage.ClaudeCacheCreation1hTokens
if relayInfo.ChannelType == constant.ChannelTypeOpenRouter {
promptTokens -= cacheTokens
@@ -269,7 +273,12 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
if !relayInfo.PriceData.UsePrice {
calculateQuota = float64(promptTokens)
calculateQuota += float64(cacheTokens) * cacheRatio
- calculateQuota += float64(cacheCreationTokens) * cacheCreationRatio
+ calculateQuota += float64(cacheCreationTokens5m) * cacheCreationRatio5m
+ calculateQuota += float64(cacheCreationTokens1h) * cacheCreationRatio1h
+ remainingCacheCreationTokens := cacheCreationTokens - cacheCreationTokens5m - cacheCreationTokens1h
+ if remainingCacheCreationTokens > 0 {
+ calculateQuota += float64(remainingCacheCreationTokens) * cacheCreationRatio
+ }
calculateQuota += float64(completionTokens) * completionRatio
calculateQuota = calculateQuota * groupRatio * modelRatio
} else {
@@ -322,7 +331,11 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
}
other := GenerateClaudeOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio,
- cacheTokens, cacheRatio, cacheCreationTokens, cacheCreationRatio, modelPrice, relayInfo.PriceData.GroupRatioInfo.GroupSpecialRatio)
+ cacheTokens, cacheRatio,
+ cacheCreationTokens, cacheCreationRatio,
+ cacheCreationTokens5m, cacheCreationRatio5m,
+ cacheCreationTokens1h, cacheCreationRatio1h,
+ modelPrice, relayInfo.PriceData.GroupRatioInfo.GroupSpecialRatio)
model.RecordConsumeLog(ctx, relayInfo.UserId, model.RecordConsumeLogParams{
ChannelId: relayInfo.ChannelId,
PromptTokens: promptTokens,
diff --git a/types/price_data.go b/types/price_data.go
index 8f6297408..93044f865 100644
--- a/types/price_data.go
+++ b/types/price_data.go
@@ -15,6 +15,8 @@ type PriceData struct {
CompletionRatio float64
CacheRatio float64
CacheCreationRatio float64
+ CacheCreation5mRatio float64
+ CacheCreation1hRatio float64
ImageRatio float64
AudioRatio float64
AudioCompletionRatio float64
@@ -31,5 +33,5 @@ type PerCallPriceData struct {
}
func (p PriceData) ToSetting() string {
- return fmt.Sprintf("ModelPrice: %f, ModelRatio: %f, CompletionRatio: %f, CacheRatio: %f, GroupRatio: %f, UsePrice: %t, CacheCreationRatio: %f, QuotaToPreConsume: %d, ImageRatio: %f, AudioRatio: %f, AudioCompletionRatio: %f", p.ModelPrice, p.ModelRatio, p.CompletionRatio, p.CacheRatio, p.GroupRatioInfo.GroupRatio, p.UsePrice, p.CacheCreationRatio, p.QuotaToPreConsume, p.ImageRatio, p.AudioRatio, p.AudioCompletionRatio)
+ return fmt.Sprintf("ModelPrice: %f, ModelRatio: %f, CompletionRatio: %f, CacheRatio: %f, GroupRatio: %f, UsePrice: %t, CacheCreationRatio: %f, CacheCreation5mRatio: %f, CacheCreation1hRatio: %f, QuotaToPreConsume: %d, ImageRatio: %f, AudioRatio: %f, AudioCompletionRatio: %f", p.ModelPrice, p.ModelRatio, p.CompletionRatio, p.CacheRatio, p.GroupRatioInfo.GroupRatio, p.UsePrice, p.CacheCreationRatio, p.CacheCreation5mRatio, p.CacheCreation1hRatio, p.QuotaToPreConsume, p.ImageRatio, p.AudioRatio, p.AudioCompletionRatio)
}
diff --git a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx
index 9af30226a..b3096c286 100644
--- a/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx
+++ b/web/src/components/table/usage-logs/UsageLogsColumnDefs.jsx
@@ -551,6 +551,10 @@ export const getLogsColumns = ({
other.cache_ratio || 1.0,
other.cache_creation_tokens || 0,
other.cache_creation_ratio || 1.0,
+ other.cache_creation_tokens_5m || 0,
+ other.cache_creation_ratio_5m || other.cache_creation_ratio || 1.0,
+ other.cache_creation_tokens_1h || 0,
+ other.cache_creation_ratio_1h || other.cache_creation_ratio || 1.0,
false,
1.0,
other?.is_system_prompt_overwritten,
@@ -565,6 +569,10 @@ export const getLogsColumns = ({
other.cache_ratio || 1.0,
0,
1.0,
+ 0,
+ 1.0,
+ 0,
+ 1.0,
false,
1.0,
other?.is_system_prompt_overwritten,
diff --git a/web/src/helpers/render.jsx b/web/src/helpers/render.jsx
index 1c3dda854..241830ab6 100644
--- a/web/src/helpers/render.jsx
+++ b/web/src/helpers/render.jsx
@@ -1046,6 +1046,10 @@ function renderPriceSimpleCore({
cacheRatio = 1.0,
cacheCreationTokens = 0,
cacheCreationRatio = 1.0,
+ cacheCreationTokens5m = 0,
+ cacheCreationRatio5m = 1.0,
+ cacheCreationTokens1h = 0,
+ cacheCreationRatio1h = 1.0,
image = false,
imageRatio = 1.0,
isSystemPromptOverride = false,
@@ -1064,17 +1068,40 @@ function renderPriceSimpleCore({
});
}
+ const hasSplitCacheCreation =
+ cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
+
+ const shouldShowLegacyCacheCreation =
+ !hasSplitCacheCreation && cacheCreationTokens !== 0;
+
+ const shouldShowCache = cacheTokens !== 0;
+ const shouldShowCacheCreation5m =
+ hasSplitCacheCreation && cacheCreationTokens5m > 0;
+ const shouldShowCacheCreation1h =
+ hasSplitCacheCreation && cacheCreationTokens1h > 0;
+
const parts = [];
// base: model ratio
parts.push(i18next.t('模型: {{ratio}}'));
// cache part (label differs when with image)
- if (cacheTokens !== 0) {
+ if (shouldShowCache) {
parts.push(i18next.t('缓存: {{cacheRatio}}'));
}
- // cache creation part (Claude specific if passed)
- if (cacheCreationTokens !== 0) {
+ if (hasSplitCacheCreation) {
+ if (shouldShowCacheCreation5m && shouldShowCacheCreation1h) {
+ parts.push(
+ i18next.t(
+ '缓存创建: 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}',
+ ),
+ );
+ } else if (shouldShowCacheCreation5m) {
+ parts.push(i18next.t('缓存创建: 5m {{cacheCreationRatio5m}}'));
+ } else if (shouldShowCacheCreation1h) {
+ parts.push(i18next.t('缓存创建: 1h {{cacheCreationRatio1h}}'));
+ }
+ } else if (shouldShowLegacyCacheCreation) {
parts.push(i18next.t('缓存创建: {{cacheCreationRatio}}'));
}
@@ -1091,6 +1118,8 @@ function renderPriceSimpleCore({
groupRatio: finalGroupRatio,
cacheRatio: cacheRatio,
cacheCreationRatio: cacheCreationRatio,
+ cacheCreationRatio5m: cacheCreationRatio5m,
+ cacheCreationRatio1h: cacheCreationRatio1h,
imageRatio: imageRatio,
});
@@ -1450,6 +1479,10 @@ export function renderModelPriceSimple(
cacheRatio = 1.0,
cacheCreationTokens = 0,
cacheCreationRatio = 1.0,
+ cacheCreationTokens5m = 0,
+ cacheCreationRatio5m = 1.0,
+ cacheCreationTokens1h = 0,
+ cacheCreationRatio1h = 1.0,
image = false,
imageRatio = 1.0,
isSystemPromptOverride = false,
@@ -1464,6 +1497,10 @@ export function renderModelPriceSimple(
cacheRatio,
cacheCreationTokens,
cacheCreationRatio,
+ cacheCreationTokens5m,
+ cacheCreationRatio5m,
+ cacheCreationTokens1h,
+ cacheCreationRatio1h,
image,
imageRatio,
isSystemPromptOverride,
@@ -1681,6 +1718,10 @@ export function renderClaudeModelPrice(
cacheRatio = 1.0,
cacheCreationTokens = 0,
cacheCreationRatio = 1.0,
+ cacheCreationTokens5m = 0,
+ cacheCreationRatio5m = 1.0,
+ cacheCreationTokens1h = 0,
+ cacheCreationRatio1h = 1.0,
) {
const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio(
groupRatio,
@@ -1710,20 +1751,121 @@ export function renderClaudeModelPrice(
const completionRatioValue = completionRatio || 0;
const inputRatioPrice = modelRatio * 2.0;
const completionRatioPrice = modelRatio * 2.0 * completionRatioValue;
- let cacheRatioPrice = (modelRatio * 2.0 * cacheRatio).toFixed(2);
- let cacheCreationRatioPrice = modelRatio * 2.0 * cacheCreationRatio;
+ const cacheRatioPrice = modelRatio * 2.0 * cacheRatio;
+ const cacheCreationRatioPrice = modelRatio * 2.0 * cacheCreationRatio;
+ const cacheCreationRatioPrice5m = modelRatio * 2.0 * cacheCreationRatio5m;
+ const cacheCreationRatioPrice1h = modelRatio * 2.0 * cacheCreationRatio1h;
+
+ const hasSplitCacheCreation =
+ cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
+
+ const shouldShowCache = cacheTokens > 0;
+ const shouldShowLegacyCacheCreation =
+ !hasSplitCacheCreation && cacheCreationTokens > 0;
+ const shouldShowCacheCreation5m =
+ hasSplitCacheCreation && cacheCreationTokens5m > 0;
+ const shouldShowCacheCreation1h =
+ hasSplitCacheCreation && cacheCreationTokens1h > 0;
// Calculate effective input tokens (non-cached + cached with ratio applied + cache creation with ratio applied)
const nonCachedTokens = inputTokens;
const effectiveInputTokens =
nonCachedTokens +
cacheTokens * cacheRatio +
- cacheCreationTokens * cacheCreationRatio;
+ cacheCreationTokens * cacheCreationRatio +
+ cacheCreationTokens5m * cacheCreationRatio5m +
+ cacheCreationTokens1h * cacheCreationRatio1h;
let price =
(effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio +
(completionTokens / 1000000) * completionRatioPrice * groupRatio;
+ const inputUnitPrice = inputRatioPrice * rate;
+ const completionUnitPrice = completionRatioPrice * rate;
+ const cacheUnitPrice = cacheRatioPrice * rate;
+ const cacheCreationUnitPrice = cacheCreationRatioPrice * rate;
+ const cacheCreationUnitPrice5m = cacheCreationRatioPrice5m * rate;
+ const cacheCreationUnitPrice1h = cacheCreationRatioPrice1h * rate;
+ const cacheCreationUnitPriceTotal =
+ cacheCreationUnitPrice5m + cacheCreationUnitPrice1h;
+
+ const breakdownSegments = [
+ i18next.t('提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}}', {
+ input: inputTokens,
+ symbol,
+ price: inputUnitPrice.toFixed(6),
+ }),
+ ];
+
+ if (shouldShowCache) {
+ breakdownSegments.push(
+ i18next.t(
+ '缓存 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})',
+ {
+ tokens: cacheTokens,
+ symbol,
+ price: cacheUnitPrice.toFixed(6),
+ ratio: cacheRatio,
+ },
+ ),
+ );
+ }
+
+ if (shouldShowLegacyCacheCreation) {
+ breakdownSegments.push(
+ i18next.t(
+ '缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})',
+ {
+ tokens: cacheCreationTokens,
+ symbol,
+ price: cacheCreationUnitPrice.toFixed(6),
+ ratio: cacheCreationRatio,
+ },
+ ),
+ );
+ }
+
+ if (shouldShowCacheCreation5m) {
+ breakdownSegments.push(
+ i18next.t(
+ '5m缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})',
+ {
+ tokens: cacheCreationTokens5m,
+ symbol,
+ price: cacheCreationUnitPrice5m.toFixed(6),
+ ratio: cacheCreationRatio5m,
+ },
+ ),
+ );
+ }
+
+ if (shouldShowCacheCreation1h) {
+ breakdownSegments.push(
+ i18next.t(
+ '1h缓存创建 {{tokens}} tokens / 1M tokens * {{symbol}}{{price}} (倍率: {{ratio}})',
+ {
+ tokens: cacheCreationTokens1h,
+ symbol,
+ price: cacheCreationUnitPrice1h.toFixed(6),
+ ratio: cacheCreationRatio1h,
+ },
+ ),
+ );
+ }
+
+ breakdownSegments.push(
+ i18next.t(
+ '补全 {{completion}} tokens / 1M tokens * {{symbol}}{{price}}',
+ {
+ completion: completionTokens,
+ symbol,
+ price: completionUnitPrice.toFixed(6),
+ },
+ ),
+ );
+
+ const breakdownText = breakdownSegments.join(' + ');
+
return (
<>
{i18next.t( '缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})', @@ -1752,13 +1894,13 @@ export function renderClaudeModelPrice( symbol: symbol, price: (inputRatioPrice * rate).toFixed(6), ratio: cacheRatio, - total: (cacheRatioPrice * rate).toFixed(2), + total: cacheUnitPrice.toFixed(6), cacheRatio: cacheRatio, }, )}
)} - {cacheCreationTokens > 0 && ( + {shouldShowLegacyCacheCreation && ({i18next.t( '缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})', @@ -1766,49 +1908,65 @@ export function renderClaudeModelPrice( symbol: symbol, price: (inputRatioPrice * rate).toFixed(6), ratio: cacheCreationRatio, - total: (cacheCreationRatioPrice * rate).toFixed(6), + total: cacheCreationUnitPrice.toFixed(6), cacheCreationRatio: cacheCreationRatio, }, )}
)} + {shouldShowCacheCreation5m && ( ++ {i18next.t( + '5m缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (5m缓存创建倍率: {{cacheCreationRatio5m}})', + { + symbol: symbol, + price: (inputRatioPrice * rate).toFixed(6), + ratio: cacheCreationRatio5m, + total: cacheCreationUnitPrice5m.toFixed(6), + cacheCreationRatio5m: cacheCreationRatio5m, + }, + )} +
+ )} + {shouldShowCacheCreation1h && ( ++ {i18next.t( + '1h缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (1h缓存创建倍率: {{cacheCreationRatio1h}})', + { + symbol: symbol, + price: (inputRatioPrice * rate).toFixed(6), + ratio: cacheCreationRatio1h, + total: cacheCreationUnitPrice1h.toFixed(6), + cacheCreationRatio1h: cacheCreationRatio1h, + }, + )} +
+ )} + {shouldShowCacheCreation5m && shouldShowCacheCreation1h && ( ++ {i18next.t( + '缓存创建价格合计:5m {{symbol}}{{five}} + 1h {{symbol}}{{one}} = {{symbol}}{{total}} / 1M tokens', + { + symbol: symbol, + five: cacheCreationUnitPrice5m.toFixed(6), + one: cacheCreationUnitPrice1h.toFixed(6), + total: cacheCreationUnitPriceTotal.toFixed(6), + }, + )} +
+ )}- {cacheTokens > 0 || cacheCreationTokens > 0 - ? i18next.t( - '提示 {{nonCacheInput}} tokens / 1M tokens * {{symbol}}{{price}} + 缓存 {{cacheInput}} tokens / 1M tokens * {{symbol}}{{cachePrice}} + 缓存创建 {{cacheCreationInput}} tokens / 1M tokens * {{symbol}}{{cacheCreationPrice}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}', - { - nonCacheInput: nonCachedTokens, - cacheInput: cacheTokens, - cacheRatio: cacheRatio, - cacheCreationInput: cacheCreationTokens, - cacheCreationRatio: cacheCreationRatio, - symbol: symbol, - cachePrice: (cacheRatioPrice * rate).toFixed(2), - cacheCreationPrice: ( - cacheCreationRatioPrice * rate - ).toFixed(6), - price: (inputRatioPrice * rate).toFixed(6), - completion: completionTokens, - compPrice: (completionRatioPrice * rate).toFixed(6), - ratio: groupRatio, - ratioType: ratioLabel, - total: (price * rate).toFixed(6), - }, - ) - : i18next.t( - '提示 {{input}} tokens / 1M tokens * {{symbol}}{{price}} + 补全 {{completion}} tokens / 1M tokens * {{symbol}}{{compPrice}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}', - { - input: inputTokens, - symbol: symbol, - price: (inputRatioPrice * rate).toFixed(6), - completion: completionTokens, - compPrice: (completionRatioPrice * rate).toFixed(6), - ratio: groupRatio, - ratioType: ratioLabel, - total: (price * rate).toFixed(6), - }, - )} + {i18next.t( + '{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}', + { + breakdown: breakdownText, + ratioType: ratioLabel, + ratio: groupRatio, + symbol: symbol, + total: (price * rate).toFixed(6), + }, + )}
{i18next.t('仅供参考,以实际扣费为准')}
@@ -1825,6 +1983,10 @@ export function renderClaudeLogContent( user_group_ratio, cacheRatio = 1.0, cacheCreationRatio = 1.0, + cacheCreationTokens5m = 0, + cacheCreationRatio5m = 1.0, + cacheCreationTokens1h = 0, + cacheCreationRatio1h = 1.0, ) { const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio( groupRatio, @@ -1843,17 +2005,58 @@ export function renderClaudeLogContent( ratio: groupRatio, }); } else { - return i18next.t( - '模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}},{{ratioType}} {{ratio}}', - { - modelRatio: modelRatio, - completionRatio: completionRatio, - cacheRatio: cacheRatio, - cacheCreationRatio: cacheCreationRatio, + const hasSplitCacheCreation = + cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0; + const shouldShowCacheCreation5m = + hasSplitCacheCreation && cacheCreationTokens5m > 0; + const shouldShowCacheCreation1h = + hasSplitCacheCreation && cacheCreationTokens1h > 0; + + let cacheCreationPart = null; + if (hasSplitCacheCreation) { + if (shouldShowCacheCreation5m && shouldShowCacheCreation1h) { + cacheCreationPart = i18next.t( + '缓存创建倍率 5m {{cacheCreationRatio5m}} / 1h {{cacheCreationRatio1h}}', + { + cacheCreationRatio5m, + cacheCreationRatio1h, + }, + ); + } else if (shouldShowCacheCreation5m) { + cacheCreationPart = i18next.t( + '缓存创建倍率 5m {{cacheCreationRatio5m}}', + { + cacheCreationRatio5m, + }, + ); + } else if (shouldShowCacheCreation1h) { + cacheCreationPart = i18next.t( + '缓存创建倍率 1h {{cacheCreationRatio1h}}', + { + cacheCreationRatio1h, + }, + ); + } + } + + if (!cacheCreationPart) { + cacheCreationPart = i18next.t('缓存创建倍率 {{cacheCreationRatio}}', { + cacheCreationRatio, + }); + } + + const parts = [ + i18next.t('模型倍率 {{modelRatio}}', { modelRatio }), + i18next.t('输出倍率 {{completionRatio}}', { completionRatio }), + i18next.t('缓存倍率 {{cacheRatio}}', { cacheRatio }), + cacheCreationPart, + i18next.t('{{ratioType}} {{ratio}}', { ratioType: ratioLabel, ratio: groupRatio, - }, - ); + }), + ]; + + return parts.join(','); } } diff --git a/web/src/hooks/usage-logs/useUsageLogsData.jsx b/web/src/hooks/usage-logs/useUsageLogsData.jsx index 6041d7427..ee3d42d69 100644 --- a/web/src/hooks/usage-logs/useUsageLogsData.jsx +++ b/web/src/hooks/usage-logs/useUsageLogsData.jsx @@ -361,6 +361,10 @@ export const useLogsData = () => { other?.user_group_ratio, other.cache_ratio || 1.0, other.cache_creation_ratio || 1.0, + other.cache_creation_tokens_5m || 0, + other.cache_creation_ratio_5m || other.cache_creation_ratio || 1.0, + other.cache_creation_tokens_1h || 0, + other.cache_creation_ratio_1h || other.cache_creation_ratio || 1.0, ) : renderLogContent( other?.model_ratio, @@ -429,6 +433,10 @@ export const useLogsData = () => { other.cache_ratio || 1.0, other.cache_creation_tokens || 0, other.cache_creation_ratio || 1.0, + other.cache_creation_tokens_5m || 0, + other.cache_creation_ratio_5m || other.cache_creation_ratio || 1.0, + other.cache_creation_tokens_1h || 0, + other.cache_creation_ratio_1h || other.cache_creation_ratio || 1.0, ); } else { content = renderModelPrice( diff --git a/web/src/i18n/locales/en.json b/web/src/i18n/locales/en.json index 50d76f609..ef233b6e0 100644 --- a/web/src/i18n/locales/en.json +++ b/web/src/i18n/locales/en.json @@ -1516,6 +1516,10 @@ "缓存倍率": "Cache ratio", "缓存创建 Tokens": "Cache Creation Tokens", "缓存创建: {{cacheCreationRatio}}": "Cache creation: {{cacheCreationRatio}}", + "缓存创建: 5m {{cacheCreationRatio5m}}": "Cache creation: 5m {{cacheCreationRatio5m}}", + "缓存创建: 1h {{cacheCreationRatio1h}}": "Cache creation: 1h {{cacheCreationRatio1h}}", + "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Cache creation multiplier 5m {{cacheCreationRatio5m}}", + "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Cache creation multiplier 1h {{cacheCreationRatio1h}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache creation ratio: {{cacheCreationRatio}})", "编辑": "Edit", "编辑API": "Edit API", @@ -2104,4 +2108,4 @@ "统一的": "The Unified", "大模型接口网关": "LLM API Gateway" } -} \ No newline at end of file +} diff --git a/web/src/i18n/locales/fr.json b/web/src/i18n/locales/fr.json index b2d0dc38d..a810ce07d 100644 --- a/web/src/i18n/locales/fr.json +++ b/web/src/i18n/locales/fr.json @@ -1525,6 +1525,10 @@ "缓存倍率": "Ratio de cache", "缓存创建 Tokens": "Jetons de création de cache", "缓存创建: {{cacheCreationRatio}}": "Création de cache : {{cacheCreationRatio}}", + "缓存创建: 5m {{cacheCreationRatio5m}}": "Création de cache : 5m {{cacheCreationRatio5m}}", + "缓存创建: 1h {{cacheCreationRatio1h}}": "Création de cache : 1h {{cacheCreationRatio1h}}", + "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Multiplicateur de création de cache 5m {{cacheCreationRatio5m}}", + "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Multiplicateur de création de cache 1h {{cacheCreationRatio1h}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Prix de création du cache : {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (taux de création de cache : {{cacheCreationRatio}})", "编辑": "Modifier", "编辑API": "Modifier l'API", diff --git a/web/src/i18n/locales/ja.json b/web/src/i18n/locales/ja.json index 7cded82eb..46456b157 100644 --- a/web/src/i18n/locales/ja.json +++ b/web/src/i18n/locales/ja.json @@ -1516,6 +1516,10 @@ "缓存倍率": "キャッシュ倍率", "缓存创建 Tokens": "キャッシュ作成トークン", "缓存创建: {{cacheCreationRatio}}": "キャッシュ作成:{{cacheCreationRatio}}", + "缓存创建: 5m {{cacheCreationRatio5m}}": "キャッシュ作成:5m {{cacheCreationRatio5m}}", + "缓存创建: 1h {{cacheCreationRatio1h}}": "キャッシュ作成:1h {{cacheCreationRatio1h}}", + "缓存创建倍率 5m {{cacheCreationRatio5m}}": "キャッシュ作成倍率 5m {{cacheCreationRatio5m}}", + "缓存创建倍率 1h {{cacheCreationRatio1h}}": "キャッシュ作成倍率 1h {{cacheCreationRatio1h}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "キャッシュ作成料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1Mtokens(キャッシュ作成倍率:{{cacheCreationRatio}})", "编辑": "編集", "编辑API": "API編集", @@ -2075,4 +2079,4 @@ "统一的": "統合型", "大模型接口网关": "LLM APIゲートウェイ" } -} \ No newline at end of file +} diff --git a/web/src/i18n/locales/ru.json b/web/src/i18n/locales/ru.json index 7162a9eb6..35abe12e0 100644 --- a/web/src/i18n/locales/ru.json +++ b/web/src/i18n/locales/ru.json @@ -1534,6 +1534,10 @@ "缓存倍率": "Коэффициент кэширования", "缓存创建 Tokens": "Создание кэша токенов", "缓存创建: {{cacheCreationRatio}}": "Создание кэша: {{cacheCreationRatio}}", + "缓存创建: 5m {{cacheCreationRatio5m}}": "Создание кэша: 5m {{cacheCreationRatio5m}}", + "缓存创建: 1h {{cacheCreationRatio1h}}": "Создание кэша: 1h {{cacheCreationRatio1h}}", + "缓存创建倍率 5m {{cacheCreationRatio5m}}": "Множитель создания кэша 5m {{cacheCreationRatio5m}}", + "缓存创建倍率 1h {{cacheCreationRatio1h}}": "Множитель создания кэша 1h {{cacheCreationRatio1h}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Цена создания кэша: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (коэффициент создания кэша: {{cacheCreationRatio}})", "编辑": "Редактировать", "编辑API": "Редактировать API", diff --git a/web/src/i18n/locales/zh.json b/web/src/i18n/locales/zh.json index bd43be977..973f3c937 100644 --- a/web/src/i18n/locales/zh.json +++ b/web/src/i18n/locales/zh.json @@ -1507,6 +1507,10 @@ "缓存倍率": "缓存倍率", "缓存创建 Tokens": "缓存创建 Tokens", "缓存创建: {{cacheCreationRatio}}": "缓存创建: {{cacheCreationRatio}}", + "缓存创建: 5m {{cacheCreationRatio5m}}": "缓存创建: 5m {{cacheCreationRatio5m}}", + "缓存创建: 1h {{cacheCreationRatio1h}}": "缓存创建: 1h {{cacheCreationRatio1h}}", + "缓存创建倍率 5m {{cacheCreationRatio5m}}": "缓存创建倍率 5m {{cacheCreationRatio5m}}", + "缓存创建倍率 1h {{cacheCreationRatio1h}}": "缓存创建倍率 1h {{cacheCreationRatio1h}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})", "编辑": "编辑", "编辑API": "编辑API", @@ -2066,4 +2070,4 @@ "Creem 介绍": "Creem 是一个简单的支付处理平台,支持固定金额产品销售,以及订阅销售。", "Creem Setting Tips": "Creem 只支持预设的固定金额产品,这产品以及价格需要提前在Creem网站内创建配置,所以不支持自定义动态金额充值。在Creem端配置产品的名字以及价格,获取Product Id 后填到下面的产品,在new-api为该产品设置充值额度,以及展示价格。" } -} \ No newline at end of file +}