90 lines
4.1 KiB
Go
90 lines
4.1 KiB
Go
package common
|
||
|
||
import "github.com/qianfree/team-api/relay/dto"
|
||
|
||
// TokenDetails Token 使用量细分
|
||
type TokenDetails struct {
|
||
CachedTokens int `json:"cached_tokens,omitempty"`
|
||
CachedCreationTokens int `json:"cached_creation_tokens,omitempty"` // Claude cache_creation_input_tokens
|
||
CachedCreation5mTokens int `json:"cached_creation_5m_tokens,omitempty"` // Claude 5分钟缓存创建
|
||
CachedCreation1hTokens int `json:"cached_creation_1h_tokens,omitempty"` // Claude 1小时缓存创建
|
||
// CacheWriteTokens 本次写入缓存的 token(OpenAI Responses cache_write_tokens,观测字段)。
|
||
// 注意:OpenAI 缓存写入按普通输入价计费(含于 PromptTokens),不得并入 CachedCreationTokens
|
||
// 计费桶(Claude 写入才有独立 CacheCreationPrice),计费引擎不读取本字段
|
||
CacheWriteTokens int `json:"cache_write_tokens,omitempty"`
|
||
AudioTokens int `json:"audio_tokens,omitempty"`
|
||
TextTokens int `json:"text_tokens,omitempty"`
|
||
ImageTokens int `json:"image_tokens,omitempty"`
|
||
ReasoningTokens int `json:"reasoning_tokens,omitempty"`
|
||
AcceptedPredictionTokens int `json:"accepted_prediction_tokens,omitempty"`
|
||
RejectedPredictionTokens int `json:"rejected_prediction_tokens,omitempty"`
|
||
}
|
||
|
||
// Usage Token 使用量信息
|
||
type Usage struct {
|
||
PromptTokens int `json:"prompt_tokens"`
|
||
CompletionTokens int `json:"completion_tokens"`
|
||
TotalTokens int `json:"total_tokens"`
|
||
CacheCreationTokens int `json:"cache_creation_tokens,omitempty"` // 缓存创建 token 总量
|
||
PromptTokensDetails *TokenDetails `json:"prompt_tokens_details,omitempty"`
|
||
CompletionTokenDetails *TokenDetails `json:"completion_token_details,omitempty"`
|
||
// CacheIncludedInPrompt 标记 PromptTokens 是否包含 cache tokens。
|
||
// true: PromptTokens 是总量,cache 是其子集,计费时需扣减避免重复计费(OpenAI 原生 API)
|
||
// false(默认): PromptTokens 与 cache 独立,不扣减(Claude API、第三方兼容 API)
|
||
CacheIncludedInPrompt bool `json:"-"`
|
||
}
|
||
|
||
// TotalInputTokens 返回「含缓存的总输入 token」:口径未包含缓存(Claude)时补加缓存读/写,
|
||
// 口径已包含(OpenAI/Gemini)时原样返回。bil_usage_logs.input_tokens 统一按此口径入库,
|
||
// 保证跨渠道 SUM 聚合语义一致(cache_read/cache_creation 列为其子集明细)。
|
||
func (u *Usage) TotalInputTokens() int {
|
||
if u == nil {
|
||
return 0
|
||
}
|
||
total := u.PromptTokens
|
||
if !u.CacheIncludedInPrompt && u.PromptTokensDetails != nil {
|
||
total += u.PromptTokensDetails.CachedTokens + u.PromptTokensDetails.CachedCreationTokens
|
||
}
|
||
return total
|
||
}
|
||
|
||
// DtoTokenDetailsToCommon 将 dto.TokenDetails 转换为 common.TokenDetails
|
||
func DtoTokenDetailsToCommon(d *dto.TokenDetails) *TokenDetails {
|
||
if d == nil {
|
||
return nil
|
||
}
|
||
return &TokenDetails{
|
||
CachedTokens: d.CachedTokens,
|
||
CachedCreationTokens: d.CachedCreationTokens,
|
||
CachedCreation5mTokens: d.CachedCreation5mTokens,
|
||
CachedCreation1hTokens: d.CachedCreation1hTokens,
|
||
CacheWriteTokens: d.CacheWriteTokens,
|
||
AudioTokens: d.AudioTokens,
|
||
TextTokens: d.TextTokens,
|
||
ImageTokens: d.ImageTokens,
|
||
ReasoningTokens: d.ReasoningTokens,
|
||
AcceptedPredictionTokens: d.AcceptedPredictionTokens,
|
||
RejectedPredictionTokens: d.RejectedPredictionTokens,
|
||
}
|
||
}
|
||
|
||
// CommonTokenDetailsToDto 将 common.TokenDetails 转换为 dto.TokenDetails
|
||
func CommonTokenDetailsToDto(c *TokenDetails) *dto.TokenDetails {
|
||
if c == nil {
|
||
return nil
|
||
}
|
||
return &dto.TokenDetails{
|
||
CachedTokens: c.CachedTokens,
|
||
CachedCreationTokens: c.CachedCreationTokens,
|
||
CachedCreation5mTokens: c.CachedCreation5mTokens,
|
||
CachedCreation1hTokens: c.CachedCreation1hTokens,
|
||
CacheWriteTokens: c.CacheWriteTokens,
|
||
AudioTokens: c.AudioTokens,
|
||
TextTokens: c.TextTokens,
|
||
ImageTokens: c.ImageTokens,
|
||
ReasoningTokens: c.ReasoningTokens,
|
||
AcceptedPredictionTokens: c.AcceptedPredictionTokens,
|
||
RejectedPredictionTokens: c.RejectedPredictionTokens,
|
||
}
|
||
}
|