feat: claude 1h cache (#2155)

* feat: claude 1h cache

* feat: claude 1h cache

* fix price
This commit is contained in:
Seefs
2025-11-04 00:20:50 +08:00
committed by GitHub
parent 00782aae88
commit df2ee649ab
15 changed files with 384 additions and 66 deletions

View File

@@ -510,11 +510,44 @@ func (c *ClaudeResponse) GetClaudeError() *types.ClaudeError {
} }
type ClaudeUsage struct { type ClaudeUsage struct {
InputTokens int `json:"input_tokens"` InputTokens int `json:"input_tokens"`
CacheCreationInputTokens int `json:"cache_creation_input_tokens"` CacheCreationInputTokens int `json:"cache_creation_input_tokens"`
CacheReadInputTokens int `json:"cache_read_input_tokens"` CacheReadInputTokens int `json:"cache_read_input_tokens"`
OutputTokens int `json:"output_tokens"` OutputTokens int `json:"output_tokens"`
ServerToolUse *ClaudeServerToolUse `json:"server_tool_use,omitempty"` 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 { type ClaudeServerToolUse struct {

View File

@@ -230,6 +230,11 @@ type Usage struct {
InputTokens int `json:"input_tokens"` InputTokens int `json:"input_tokens"`
OutputTokens int `json:"output_tokens"` OutputTokens int `json:"output_tokens"`
InputTokensDetails *InputTokenDetails `json:"input_tokens_details"` 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 // OpenRouter Params
Cost any `json:"cost,omitempty"` Cost any `json:"cost,omitempty"`
} }

View File

@@ -596,6 +596,8 @@ func FormatClaudeResponseInfo(requestMode int, claudeResponse *dto.ClaudeRespons
claudeInfo.Usage.PromptTokens = claudeResponse.Message.Usage.InputTokens claudeInfo.Usage.PromptTokens = claudeResponse.Message.Usage.InputTokens
claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Message.Usage.CacheReadInputTokens claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Message.Usage.CacheReadInputTokens
claudeInfo.Usage.PromptTokensDetails.CachedCreationTokens = claudeResponse.Message.Usage.CacheCreationInputTokens 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 claudeInfo.Usage.CompletionTokens = claudeResponse.Message.Usage.OutputTokens
} else if claudeResponse.Type == "content_block_delta" { } else if claudeResponse.Type == "content_block_delta" {
if claudeResponse.Delta.Text != nil { 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.TotalTokens = claudeResponse.Usage.InputTokens + claudeResponse.Usage.OutputTokens
claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Usage.CacheReadInputTokens claudeInfo.Usage.PromptTokensDetails.CachedTokens = claudeResponse.Usage.CacheReadInputTokens
claudeInfo.Usage.PromptTokensDetails.CachedCreationTokens = claudeResponse.Usage.CacheCreationInputTokens claudeInfo.Usage.PromptTokensDetails.CachedCreationTokens = claudeResponse.Usage.CacheCreationInputTokens
claudeInfo.Usage.ClaudeCacheCreation5mTokens = claudeResponse.Usage.GetCacheCreation5mTokens()
claudeInfo.Usage.ClaudeCacheCreation1hTokens = claudeResponse.Usage.GetCacheCreation1hTokens()
} }
var responseData []byte var responseData []byte
switch info.RelayFormat { switch info.RelayFormat {

View File

@@ -13,6 +13,9 @@ import (
"github.com/gin-gonic/gin" "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 // 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 { func HandleGroupRatio(ctx *gin.Context, relayInfo *relaycommon.RelayInfo) types.GroupRatioInfo {
groupRatioInfo := types.GroupRatioInfo{ groupRatioInfo := types.GroupRatioInfo{
@@ -53,6 +56,8 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
var cacheRatio float64 var cacheRatio float64
var imageRatio float64 var imageRatio float64
var cacheCreationRatio float64 var cacheCreationRatio float64
var cacheCreationRatio5m float64
var cacheCreationRatio1h float64
var audioRatio float64 var audioRatio float64
var audioCompletionRatio float64 var audioCompletionRatio float64
var freeModel bool var freeModel bool
@@ -76,6 +81,9 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName) completionRatio = ratio_setting.GetCompletionRatio(info.OriginModelName)
cacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName) cacheRatio, _ = ratio_setting.GetCacheRatio(info.OriginModelName)
cacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName) cacheCreationRatio, _ = ratio_setting.GetCreateCacheRatio(info.OriginModelName)
cacheCreationRatio5m = cacheCreationRatio
// 固定1h和5min缓存写入价格的比例
cacheCreationRatio1h = cacheCreationRatio * claudeCacheCreation1hMultiplier
imageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName) imageRatio, _ = ratio_setting.GetImageRatio(info.OriginModelName)
audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName) audioRatio = ratio_setting.GetAudioRatio(info.OriginModelName)
audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName) audioCompletionRatio = ratio_setting.GetAudioCompletionRatio(info.OriginModelName)
@@ -116,6 +124,8 @@ func ModelPriceHelper(c *gin.Context, info *relaycommon.RelayInfo, promptTokens
AudioRatio: audioRatio, AudioRatio: audioRatio,
AudioCompletionRatio: audioCompletionRatio, AudioCompletionRatio: audioCompletionRatio,
CacheCreationRatio: cacheCreationRatio, CacheCreationRatio: cacheCreationRatio,
CacheCreation5mRatio: cacheCreationRatio5m,
CacheCreation1hRatio: cacheCreationRatio1h,
QuotaToPreConsume: preConsumedQuota, QuotaToPreConsume: preConsumedQuota,
} }

View File

@@ -92,11 +92,23 @@ func GenerateAudioOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
} }
func GenerateClaudeOtherInfo(ctx *gin.Context, relayInfo *relaycommon.RelayInfo, modelRatio, groupRatio, completionRatio float64, 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 := GenerateTextOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, cacheTokens, cacheRatio, modelPrice, userGroupRatio)
info["claude"] = true info["claude"] = true
info["cache_creation_tokens"] = cacheCreationTokens info["cache_creation_tokens"] = cacheCreationTokens
info["cache_creation_ratio"] = cacheCreationRatio 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 return info
} }

View File

@@ -251,7 +251,11 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
cacheTokens := usage.PromptTokensDetails.CachedTokens cacheTokens := usage.PromptTokensDetails.CachedTokens
cacheCreationRatio := relayInfo.PriceData.CacheCreationRatio cacheCreationRatio := relayInfo.PriceData.CacheCreationRatio
cacheCreationRatio5m := relayInfo.PriceData.CacheCreation5mRatio
cacheCreationRatio1h := relayInfo.PriceData.CacheCreation1hRatio
cacheCreationTokens := usage.PromptTokensDetails.CachedCreationTokens cacheCreationTokens := usage.PromptTokensDetails.CachedCreationTokens
cacheCreationTokens5m := usage.ClaudeCacheCreation5mTokens
cacheCreationTokens1h := usage.ClaudeCacheCreation1hTokens
if relayInfo.ChannelType == constant.ChannelTypeOpenRouter { if relayInfo.ChannelType == constant.ChannelTypeOpenRouter {
promptTokens -= cacheTokens promptTokens -= cacheTokens
@@ -269,7 +273,12 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
if !relayInfo.PriceData.UsePrice { if !relayInfo.PriceData.UsePrice {
calculateQuota = float64(promptTokens) calculateQuota = float64(promptTokens)
calculateQuota += float64(cacheTokens) * cacheRatio 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 += float64(completionTokens) * completionRatio
calculateQuota = calculateQuota * groupRatio * modelRatio calculateQuota = calculateQuota * groupRatio * modelRatio
} else { } else {
@@ -322,7 +331,11 @@ func PostClaudeConsumeQuota(ctx *gin.Context, relayInfo *relaycommon.RelayInfo,
} }
other := GenerateClaudeOtherInfo(ctx, relayInfo, modelRatio, groupRatio, completionRatio, 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{ model.RecordConsumeLog(ctx, relayInfo.UserId, model.RecordConsumeLogParams{
ChannelId: relayInfo.ChannelId, ChannelId: relayInfo.ChannelId,
PromptTokens: promptTokens, PromptTokens: promptTokens,

View File

@@ -15,6 +15,8 @@ type PriceData struct {
CompletionRatio float64 CompletionRatio float64
CacheRatio float64 CacheRatio float64
CacheCreationRatio float64 CacheCreationRatio float64
CacheCreation5mRatio float64
CacheCreation1hRatio float64
ImageRatio float64 ImageRatio float64
AudioRatio float64 AudioRatio float64
AudioCompletionRatio float64 AudioCompletionRatio float64
@@ -31,5 +33,5 @@ type PerCallPriceData struct {
} }
func (p PriceData) ToSetting() string { 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)
} }

View File

@@ -551,6 +551,10 @@ export const getLogsColumns = ({
other.cache_ratio || 1.0, other.cache_ratio || 1.0,
other.cache_creation_tokens || 0, other.cache_creation_tokens || 0,
other.cache_creation_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,
false, false,
1.0, 1.0,
other?.is_system_prompt_overwritten, other?.is_system_prompt_overwritten,
@@ -565,6 +569,10 @@ export const getLogsColumns = ({
other.cache_ratio || 1.0, other.cache_ratio || 1.0,
0, 0,
1.0, 1.0,
0,
1.0,
0,
1.0,
false, false,
1.0, 1.0,
other?.is_system_prompt_overwritten, other?.is_system_prompt_overwritten,

View File

@@ -1046,6 +1046,10 @@ function renderPriceSimpleCore({
cacheRatio = 1.0, cacheRatio = 1.0,
cacheCreationTokens = 0, cacheCreationTokens = 0,
cacheCreationRatio = 1.0, cacheCreationRatio = 1.0,
cacheCreationTokens5m = 0,
cacheCreationRatio5m = 1.0,
cacheCreationTokens1h = 0,
cacheCreationRatio1h = 1.0,
image = false, image = false,
imageRatio = 1.0, imageRatio = 1.0,
isSystemPromptOverride = false, 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 = []; const parts = [];
// base: model ratio // base: model ratio
parts.push(i18next.t('模型: {{ratio}}')); parts.push(i18next.t('模型: {{ratio}}'));
// cache part (label differs when with image) // cache part (label differs when with image)
if (cacheTokens !== 0) { if (shouldShowCache) {
parts.push(i18next.t('缓存: {{cacheRatio}}')); parts.push(i18next.t('缓存: {{cacheRatio}}'));
} }
// cache creation part (Claude specific if passed) if (hasSplitCacheCreation) {
if (cacheCreationTokens !== 0) { 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}}')); parts.push(i18next.t('缓存创建: {{cacheCreationRatio}}'));
} }
@@ -1091,6 +1118,8 @@ function renderPriceSimpleCore({
groupRatio: finalGroupRatio, groupRatio: finalGroupRatio,
cacheRatio: cacheRatio, cacheRatio: cacheRatio,
cacheCreationRatio: cacheCreationRatio, cacheCreationRatio: cacheCreationRatio,
cacheCreationRatio5m: cacheCreationRatio5m,
cacheCreationRatio1h: cacheCreationRatio1h,
imageRatio: imageRatio, imageRatio: imageRatio,
}); });
@@ -1450,6 +1479,10 @@ export function renderModelPriceSimple(
cacheRatio = 1.0, cacheRatio = 1.0,
cacheCreationTokens = 0, cacheCreationTokens = 0,
cacheCreationRatio = 1.0, cacheCreationRatio = 1.0,
cacheCreationTokens5m = 0,
cacheCreationRatio5m = 1.0,
cacheCreationTokens1h = 0,
cacheCreationRatio1h = 1.0,
image = false, image = false,
imageRatio = 1.0, imageRatio = 1.0,
isSystemPromptOverride = false, isSystemPromptOverride = false,
@@ -1464,6 +1497,10 @@ export function renderModelPriceSimple(
cacheRatio, cacheRatio,
cacheCreationTokens, cacheCreationTokens,
cacheCreationRatio, cacheCreationRatio,
cacheCreationTokens5m,
cacheCreationRatio5m,
cacheCreationTokens1h,
cacheCreationRatio1h,
image, image,
imageRatio, imageRatio,
isSystemPromptOverride, isSystemPromptOverride,
@@ -1681,6 +1718,10 @@ export function renderClaudeModelPrice(
cacheRatio = 1.0, cacheRatio = 1.0,
cacheCreationTokens = 0, cacheCreationTokens = 0,
cacheCreationRatio = 1.0, cacheCreationRatio = 1.0,
cacheCreationTokens5m = 0,
cacheCreationRatio5m = 1.0,
cacheCreationTokens1h = 0,
cacheCreationRatio1h = 1.0,
) { ) {
const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio( const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio(
groupRatio, groupRatio,
@@ -1710,20 +1751,121 @@ export function renderClaudeModelPrice(
const completionRatioValue = completionRatio || 0; const completionRatioValue = completionRatio || 0;
const inputRatioPrice = modelRatio * 2.0; const inputRatioPrice = modelRatio * 2.0;
const completionRatioPrice = modelRatio * 2.0 * completionRatioValue; const completionRatioPrice = modelRatio * 2.0 * completionRatioValue;
let cacheRatioPrice = (modelRatio * 2.0 * cacheRatio).toFixed(2); const cacheRatioPrice = modelRatio * 2.0 * cacheRatio;
let cacheCreationRatioPrice = modelRatio * 2.0 * cacheCreationRatio; 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) // Calculate effective input tokens (non-cached + cached with ratio applied + cache creation with ratio applied)
const nonCachedTokens = inputTokens; const nonCachedTokens = inputTokens;
const effectiveInputTokens = const effectiveInputTokens =
nonCachedTokens + nonCachedTokens +
cacheTokens * cacheRatio + cacheTokens * cacheRatio +
cacheCreationTokens * cacheCreationRatio; cacheCreationTokens * cacheCreationRatio +
cacheCreationTokens5m * cacheCreationRatio5m +
cacheCreationTokens1h * cacheCreationRatio1h;
let price = let price =
(effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio + (effectiveInputTokens / 1000000) * inputRatioPrice * groupRatio +
(completionTokens / 1000000) * completionRatioPrice * 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 ( return (
<> <>
<article> <article>
@@ -1744,7 +1886,7 @@ export function renderClaudeModelPrice(
}, },
)} )}
</p> </p>
{cacheTokens > 0 && ( {shouldShowCache && (
<p> <p>
{i18next.t( {i18next.t(
'缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})', '缓存价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存倍率: {{cacheRatio}})',
@@ -1752,13 +1894,13 @@ export function renderClaudeModelPrice(
symbol: symbol, symbol: symbol,
price: (inputRatioPrice * rate).toFixed(6), price: (inputRatioPrice * rate).toFixed(6),
ratio: cacheRatio, ratio: cacheRatio,
total: (cacheRatioPrice * rate).toFixed(2), total: cacheUnitPrice.toFixed(6),
cacheRatio: cacheRatio, cacheRatio: cacheRatio,
}, },
)} )}
</p> </p>
)} )}
{cacheCreationTokens > 0 && ( {shouldShowLegacyCacheCreation && (
<p> <p>
{i18next.t( {i18next.t(
'缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})', '缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})',
@@ -1766,49 +1908,65 @@ export function renderClaudeModelPrice(
symbol: symbol, symbol: symbol,
price: (inputRatioPrice * rate).toFixed(6), price: (inputRatioPrice * rate).toFixed(6),
ratio: cacheCreationRatio, ratio: cacheCreationRatio,
total: (cacheCreationRatioPrice * rate).toFixed(6), total: cacheCreationUnitPrice.toFixed(6),
cacheCreationRatio: cacheCreationRatio, cacheCreationRatio: cacheCreationRatio,
}, },
)} )}
</p> </p>
)} )}
{shouldShowCacheCreation5m && (
<p>
{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,
},
)}
</p>
)}
{shouldShowCacheCreation1h && (
<p>
{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,
},
)}
</p>
)}
{shouldShowCacheCreation5m && shouldShowCacheCreation1h && (
<p>
{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),
},
)}
</p>
)}
<p></p> <p></p>
<p> <p>
{cacheTokens > 0 || cacheCreationTokens > 0 {i18next.t(
? i18next.t( '{{breakdown}} * {{ratioType}} {{ratio}} = {{symbol}}{{total}}',
'提示 {{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}}', {
{ breakdown: breakdownText,
nonCacheInput: nonCachedTokens, ratioType: ratioLabel,
cacheInput: cacheTokens, ratio: groupRatio,
cacheRatio: cacheRatio, symbol: symbol,
cacheCreationInput: cacheCreationTokens, total: (price * rate).toFixed(6),
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),
},
)}
</p> </p>
<p>{i18next.t('仅供参考,以实际扣费为准')}</p> <p>{i18next.t('仅供参考,以实际扣费为准')}</p>
</article> </article>
@@ -1825,6 +1983,10 @@ export function renderClaudeLogContent(
user_group_ratio, user_group_ratio,
cacheRatio = 1.0, cacheRatio = 1.0,
cacheCreationRatio = 1.0, cacheCreationRatio = 1.0,
cacheCreationTokens5m = 0,
cacheCreationRatio5m = 1.0,
cacheCreationTokens1h = 0,
cacheCreationRatio1h = 1.0,
) { ) {
const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio( const { ratio: effectiveGroupRatio, label: ratioLabel } = getEffectiveRatio(
groupRatio, groupRatio,
@@ -1843,17 +2005,58 @@ export function renderClaudeLogContent(
ratio: groupRatio, ratio: groupRatio,
}); });
} else { } else {
return i18next.t( const hasSplitCacheCreation =
'模型倍率 {{modelRatio}},输出倍率 {{completionRatio}},缓存倍率 {{cacheRatio}},缓存创建倍率 {{cacheCreationRatio}}{{ratioType}} {{ratio}}', cacheCreationTokens5m > 0 || cacheCreationTokens1h > 0;
{ const shouldShowCacheCreation5m =
modelRatio: modelRatio, hasSplitCacheCreation && cacheCreationTokens5m > 0;
completionRatio: completionRatio, const shouldShowCacheCreation1h =
cacheRatio: cacheRatio, hasSplitCacheCreation && cacheCreationTokens1h > 0;
cacheCreationRatio: cacheCreationRatio,
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, ratioType: ratioLabel,
ratio: groupRatio, ratio: groupRatio,
}, }),
); ];
return parts.join('');
} }
} }

View File

@@ -361,6 +361,10 @@ export const useLogsData = () => {
other?.user_group_ratio, other?.user_group_ratio,
other.cache_ratio || 1.0, other.cache_ratio || 1.0,
other.cache_creation_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( : renderLogContent(
other?.model_ratio, other?.model_ratio,
@@ -429,6 +433,10 @@ export const useLogsData = () => {
other.cache_ratio || 1.0, other.cache_ratio || 1.0,
other.cache_creation_tokens || 0, other.cache_creation_tokens || 0,
other.cache_creation_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,
); );
} else { } else {
content = renderModelPrice( content = renderModelPrice(

View File

@@ -1516,6 +1516,10 @@
"缓存倍率": "Cache ratio", "缓存倍率": "Cache ratio",
"缓存创建 Tokens": "Cache Creation Tokens", "缓存创建 Tokens": "Cache Creation Tokens",
"缓存创建: {{cacheCreationRatio}}": "Cache creation: {{cacheCreationRatio}}", "缓存创建: {{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}})", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Cache creation price: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (Cache creation ratio: {{cacheCreationRatio}})",
"编辑": "Edit", "编辑": "Edit",
"编辑API": "Edit API", "编辑API": "Edit API",
@@ -2104,4 +2108,4 @@
"统一的": "The Unified", "统一的": "The Unified",
"大模型接口网关": "LLM API Gateway" "大模型接口网关": "LLM API Gateway"
} }
} }

View File

@@ -1525,6 +1525,10 @@
"缓存倍率": "Ratio de cache", "缓存倍率": "Ratio de cache",
"缓存创建 Tokens": "Jetons de création de cache", "缓存创建 Tokens": "Jetons de création de cache",
"缓存创建: {{cacheCreationRatio}}": "Création de cache : {{cacheCreationRatio}}", "缓存创建: {{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}})", "缓存创建价格:{{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", "编辑": "Modifier",
"编辑API": "Modifier l'API", "编辑API": "Modifier l'API",

View File

@@ -1516,6 +1516,10 @@
"缓存倍率": "キャッシュ倍率", "缓存倍率": "キャッシュ倍率",
"缓存创建 Tokens": "キャッシュ作成トークン", "缓存创建 Tokens": "キャッシュ作成トークン",
"缓存创建: {{cacheCreationRatio}}": "キャッシュ作成:{{cacheCreationRatio}}", "缓存创建: {{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}}", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "キャッシュ作成料金:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1Mtokensキャッシュ作成倍率{{cacheCreationRatio}}",
"编辑": "編集", "编辑": "編集",
"编辑API": "API編集", "编辑API": "API編集",
@@ -2075,4 +2079,4 @@
"统一的": "統合型", "统一的": "統合型",
"大模型接口网关": "LLM APIゲートウェイ" "大模型接口网关": "LLM APIゲートウェイ"
} }
} }

View File

@@ -1534,6 +1534,10 @@
"缓存倍率": "Коэффициент кэширования", "缓存倍率": "Коэффициент кэширования",
"缓存创建 Tokens": "Создание кэша токенов", "缓存创建 Tokens": "Создание кэша токенов",
"缓存创建: {{cacheCreationRatio}}": "Создание кэша: {{cacheCreationRatio}}", "缓存创建: {{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}})", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "Цена создания кэша: {{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M токенов (коэффициент создания кэша: {{cacheCreationRatio}})",
"编辑": "Редактировать", "编辑": "Редактировать",
"编辑API": "Редактировать API", "编辑API": "Редактировать API",

View File

@@ -1507,6 +1507,10 @@
"缓存倍率": "缓存倍率", "缓存倍率": "缓存倍率",
"缓存创建 Tokens": "缓存创建 Tokens", "缓存创建 Tokens": "缓存创建 Tokens",
"缓存创建: {{cacheCreationRatio}}": "缓存创建: {{cacheCreationRatio}}", "缓存创建: {{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}})", "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})": "缓存创建价格:{{symbol}}{{price}} * {{ratio}} = {{symbol}}{{total}} / 1M tokens (缓存创建倍率: {{cacheCreationRatio}})",
"编辑": "编辑", "编辑": "编辑",
"编辑API": "编辑API", "编辑API": "编辑API",
@@ -2066,4 +2070,4 @@
"Creem 介绍": "Creem 是一个简单的支付处理平台,支持固定金额产品销售,以及订阅销售。", "Creem 介绍": "Creem 是一个简单的支付处理平台,支持固定金额产品销售,以及订阅销售。",
"Creem Setting Tips": "Creem 只支持预设的固定金额产品这产品以及价格需要提前在Creem网站内创建配置所以不支持自定义动态金额充值。在Creem端配置产品的名字以及价格获取Product Id 后填到下面的产品在new-api为该产品设置充值额度以及展示价格。" "Creem Setting Tips": "Creem 只支持预设的固定金额产品这产品以及价格需要提前在Creem网站内创建配置所以不支持自定义动态金额充值。在Creem端配置产品的名字以及价格获取Product Id 后填到下面的产品在new-api为该产品设置充值额度以及展示价格。"
} }
} }