174 lines
3.9 KiB
Go
174 lines
3.9 KiB
Go
package service
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
"time"
|
||
)
|
||
|
||
const (
|
||
BillingTypeBalance int8 = 0 // 钱包余额
|
||
BillingTypeSubscription int8 = 1 // 订阅套餐
|
||
)
|
||
|
||
type RequestType int16
|
||
|
||
const (
|
||
RequestTypeUnknown RequestType = 0
|
||
RequestTypeSync RequestType = 1
|
||
RequestTypeStream RequestType = 2
|
||
RequestTypeWSV2 RequestType = 3
|
||
)
|
||
|
||
func (t RequestType) IsValid() bool {
|
||
switch t {
|
||
case RequestTypeUnknown, RequestTypeSync, RequestTypeStream, RequestTypeWSV2:
|
||
return true
|
||
default:
|
||
return false
|
||
}
|
||
}
|
||
|
||
func (t RequestType) Normalize() RequestType {
|
||
if t.IsValid() {
|
||
return t
|
||
}
|
||
return RequestTypeUnknown
|
||
}
|
||
|
||
func (t RequestType) String() string {
|
||
switch t.Normalize() {
|
||
case RequestTypeSync:
|
||
return "sync"
|
||
case RequestTypeStream:
|
||
return "stream"
|
||
case RequestTypeWSV2:
|
||
return "ws_v2"
|
||
default:
|
||
return "unknown"
|
||
}
|
||
}
|
||
|
||
func RequestTypeFromInt16(v int16) RequestType {
|
||
return RequestType(v).Normalize()
|
||
}
|
||
|
||
func ParseUsageRequestType(value string) (RequestType, error) {
|
||
switch strings.ToLower(strings.TrimSpace(value)) {
|
||
case "unknown":
|
||
return RequestTypeUnknown, nil
|
||
case "sync":
|
||
return RequestTypeSync, nil
|
||
case "stream":
|
||
return RequestTypeStream, nil
|
||
case "ws_v2":
|
||
return RequestTypeWSV2, nil
|
||
default:
|
||
return RequestTypeUnknown, fmt.Errorf("invalid request_type, allowed values: unknown, sync, stream, ws_v2")
|
||
}
|
||
}
|
||
|
||
func RequestTypeFromLegacy(stream bool, openAIWSMode bool) RequestType {
|
||
if openAIWSMode {
|
||
return RequestTypeWSV2
|
||
}
|
||
if stream {
|
||
return RequestTypeStream
|
||
}
|
||
return RequestTypeSync
|
||
}
|
||
|
||
func ApplyLegacyRequestFields(requestType RequestType, fallbackStream bool, fallbackOpenAIWSMode bool) (stream bool, openAIWSMode bool) {
|
||
switch requestType.Normalize() {
|
||
case RequestTypeSync:
|
||
return false, false
|
||
case RequestTypeStream:
|
||
return true, false
|
||
case RequestTypeWSV2:
|
||
return true, true
|
||
default:
|
||
return fallbackStream, fallbackOpenAIWSMode
|
||
}
|
||
}
|
||
|
||
type UsageLog struct {
|
||
ID int64
|
||
UserID int64
|
||
APIKeyID int64
|
||
AccountID int64
|
||
RequestID string
|
||
Model string
|
||
// ReasoningEffort is the request's reasoning effort level (OpenAI Responses API),
|
||
// e.g. "low" / "medium" / "high" / "xhigh". Nil means not provided / not applicable.
|
||
ReasoningEffort *string
|
||
|
||
GroupID *int64
|
||
SubscriptionID *int64
|
||
|
||
InputTokens int
|
||
OutputTokens int
|
||
CacheCreationTokens int
|
||
CacheReadTokens int
|
||
|
||
CacheCreation5mTokens int `gorm:"column:cache_creation_5m_tokens"`
|
||
CacheCreation1hTokens int `gorm:"column:cache_creation_1h_tokens"`
|
||
|
||
InputCost float64
|
||
OutputCost float64
|
||
CacheCreationCost float64
|
||
CacheReadCost float64
|
||
TotalCost float64
|
||
ActualCost float64
|
||
RateMultiplier float64
|
||
// AccountRateMultiplier 账号计费倍率快照(nil 表示历史数据,按 1.0 处理)
|
||
AccountRateMultiplier *float64
|
||
|
||
BillingType int8
|
||
RequestType RequestType
|
||
Stream bool
|
||
OpenAIWSMode bool
|
||
DurationMs *int
|
||
FirstTokenMs *int
|
||
UserAgent *string
|
||
IPAddress *string
|
||
|
||
// Cache TTL Override 标记(管理员强制替换了缓存 TTL 计费)
|
||
CacheTTLOverridden bool
|
||
|
||
// 图片生成字段
|
||
ImageCount int
|
||
ImageSize *string
|
||
MediaType *string
|
||
|
||
CreatedAt time.Time
|
||
|
||
User *User
|
||
APIKey *APIKey
|
||
Account *Account
|
||
Group *Group
|
||
Subscription *UserSubscription
|
||
}
|
||
|
||
func (u *UsageLog) TotalTokens() int {
|
||
return u.InputTokens + u.OutputTokens + u.CacheCreationTokens + u.CacheReadTokens
|
||
}
|
||
|
||
func (u *UsageLog) EffectiveRequestType() RequestType {
|
||
if u == nil {
|
||
return RequestTypeUnknown
|
||
}
|
||
if normalized := u.RequestType.Normalize(); normalized != RequestTypeUnknown {
|
||
return normalized
|
||
}
|
||
return RequestTypeFromLegacy(u.Stream, u.OpenAIWSMode)
|
||
}
|
||
|
||
func (u *UsageLog) SyncRequestTypeAndLegacyFields() {
|
||
if u == nil {
|
||
return
|
||
}
|
||
requestType := u.EffectiveRequestType()
|
||
u.RequestType = requestType
|
||
u.Stream, u.OpenAIWSMode = ApplyLegacyRequestFields(requestType, u.Stream, u.OpenAIWSMode)
|
||
}
|