Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
陈曦
sub2api
Commits
fad04ca9
Commit
fad04ca9
authored
Feb 18, 2026
by
yangjianbo
Browse files
Merge branch 'main' of
https://github.com/mt21625457/aicodex2api
parents
1cf51b14
074bd0df
Changes
31
Hide whitespace changes
Inline
Side-by-side
backend/cmd/server/VERSION
View file @
fad04ca9
0.1.
74.2
0.1.
83.1
backend/ent/migrate/schema.go
View file @
fad04ca9
...
...
@@ -650,6 +650,7 @@ var (
{
Name
:
"ip_address"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
45
},
{
Name
:
"image_count"
,
Type
:
field
.
TypeInt
,
Default
:
0
},
{
Name
:
"image_size"
,
Type
:
field
.
TypeString
,
Nullable
:
true
,
Size
:
10
},
{
Name
:
"cache_ttl_overridden"
,
Type
:
field
.
TypeBool
,
Default
:
false
},
{
Name
:
"created_at"
,
Type
:
field
.
TypeTime
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"timestamptz"
}},
{
Name
:
"api_key_id"
,
Type
:
field
.
TypeInt64
},
{
Name
:
"account_id"
,
Type
:
field
.
TypeInt64
},
...
...
@@ -665,31 +666,31 @@ var (
ForeignKeys
:
[]
*
schema
.
ForeignKey
{
{
Symbol
:
"usage_logs_api_keys_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
RefColumns
:
[]
*
schema
.
Column
{
APIKeysColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
},
{
Symbol
:
"usage_logs_accounts_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
RefColumns
:
[]
*
schema
.
Column
{
AccountsColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
},
{
Symbol
:
"usage_logs_groups_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
9
]},
RefColumns
:
[]
*
schema
.
Column
{
GroupsColumns
[
0
]},
OnDelete
:
schema
.
SetNull
,
},
{
Symbol
:
"usage_logs_users_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
]},
RefColumns
:
[]
*
schema
.
Column
{
UsersColumns
[
0
]},
OnDelete
:
schema
.
NoAction
,
},
{
Symbol
:
"usage_logs_user_subscriptions_usage_logs"
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
0
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
1
]},
RefColumns
:
[]
*
schema
.
Column
{
UserSubscriptionsColumns
[
0
]},
OnDelete
:
schema
.
SetNull
,
},
...
...
@@ -698,32 +699,32 @@ var (
{
Name
:
"usagelog_user_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
]},
},
{
Name
:
"usagelog_api_key_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
},
{
Name
:
"usagelog_account_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
},
{
Name
:
"usagelog_group_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
8
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
9
]},
},
{
Name
:
"usagelog_subscription_id"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
0
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
3
1
]},
},
{
Name
:
"usagelog_created_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
]},
},
{
Name
:
"usagelog_model"
,
...
...
@@ -738,12 +739,12 @@ var (
{
Name
:
"usagelog_user_id_created_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
29
],
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
30
],
UsageLogsColumns
[
2
6
]},
},
{
Name
:
"usagelog_api_key_id_created_at"
,
Unique
:
false
,
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
6
],
UsageLogsColumns
[
2
5
]},
Columns
:
[]
*
schema
.
Column
{
UsageLogsColumns
[
2
7
],
UsageLogsColumns
[
2
6
]},
},
},
}
...
...
backend/ent/mutation.go
View file @
fad04ca9
...
...
@@ -15061,6 +15061,7 @@ type UsageLogMutation struct {
image_count *int
addimage_count *int
image_size *string
cache_ttl_overridden *bool
created_at *time.Time
clearedFields map[string]struct{}
user *int64
...
...
@@ -16687,6 +16688,42 @@ func (m *UsageLogMutation) ResetImageSize() {
delete(m.clearedFields, usagelog.FieldImageSize)
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func (m *UsageLogMutation) SetCacheTTLOverridden(b bool) {
m.cache_ttl_overridden = &b
}
// CacheTTLOverridden returns the value of the "cache_ttl_overridden" field in the mutation.
func (m *UsageLogMutation) CacheTTLOverridden() (r bool, exists bool) {
v := m.cache_ttl_overridden
if v == nil {
return
}
return *v, true
}
// OldCacheTTLOverridden returns the old "cache_ttl_overridden" field's value of the UsageLog entity.
// If the UsageLog object wasn't provided to the builder, the object is fetched from the database.
// An error is returned if the mutation operation is not UpdateOne, or the database query fails.
func (m *UsageLogMutation) OldCacheTTLOverridden(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldCacheTTLOverridden is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldCacheTTLOverridden requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldCacheTTLOverridden: %w", err)
}
return oldValue.CacheTTLOverridden, nil
}
// ResetCacheTTLOverridden resets all changes to the "cache_ttl_overridden" field.
func (m *UsageLogMutation) ResetCacheTTLOverridden() {
m.cache_ttl_overridden = nil
}
// SetCreatedAt sets the "created_at" field.
func (m *UsageLogMutation) SetCreatedAt(t time.Time) {
m.created_at = &t
...
...
@@ -16892,7 +16929,7 @@ func (m *UsageLogMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *UsageLogMutation) Fields() []string {
fields := make([]string, 0, 3
0
)
fields := make([]string, 0, 3
1
)
if m.user != nil {
fields = append(fields, usagelog.FieldUserID)
}
...
...
@@ -16980,6 +17017,9 @@ func (m *UsageLogMutation) Fields() []string {
if m.image_size != nil {
fields = append(fields, usagelog.FieldImageSize)
}
if m.cache_ttl_overridden != nil {
fields = append(fields, usagelog.FieldCacheTTLOverridden)
}
if m.created_at != nil {
fields = append(fields, usagelog.FieldCreatedAt)
}
...
...
@@ -17049,6 +17089,8 @@ func (m *UsageLogMutation) Field(name string) (ent.Value, bool) {
return m.ImageCount()
case usagelog.FieldImageSize:
return m.ImageSize()
case usagelog.FieldCacheTTLOverridden:
return m.CacheTTLOverridden()
case usagelog.FieldCreatedAt:
return m.CreatedAt()
}
...
...
@@ -17118,6 +17160,8 @@ func (m *UsageLogMutation) OldField(ctx context.Context, name string) (ent.Value
return m.OldImageCount(ctx)
case usagelog.FieldImageSize:
return m.OldImageSize(ctx)
case usagelog.FieldCacheTTLOverridden:
return m.OldCacheTTLOverridden(ctx)
case usagelog.FieldCreatedAt:
return m.OldCreatedAt(ctx)
}
...
...
@@ -17332,6 +17376,13 @@ func (m *UsageLogMutation) SetField(name string, value ent.Value) error {
}
m.SetImageSize(v)
return nil
case usagelog.FieldCacheTTLOverridden:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetCacheTTLOverridden(v)
return nil
case usagelog.FieldCreatedAt:
v, ok := value.(time.Time)
if !ok {
...
...
@@ -17745,6 +17796,9 @@ func (m *UsageLogMutation) ResetField(name string) error {
case usagelog.FieldImageSize:
m.ResetImageSize()
return nil
case usagelog.FieldCacheTTLOverridden:
m.ResetCacheTTLOverridden()
return nil
case usagelog.FieldCreatedAt:
m.ResetCreatedAt()
return nil
...
...
backend/ent/runtime/runtime.go
View file @
fad04ca9
...
...
@@ -779,8 +779,12 @@ func init() {
usagelogDescImageSize
:=
usagelogFields
[
28
]
.
Descriptor
()
// usagelog.ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
usagelog
.
ImageSizeValidator
=
usagelogDescImageSize
.
Validators
[
0
]
.
(
func
(
string
)
error
)
// usagelogDescCacheTTLOverridden is the schema descriptor for cache_ttl_overridden field.
usagelogDescCacheTTLOverridden
:=
usagelogFields
[
29
]
.
Descriptor
()
// usagelog.DefaultCacheTTLOverridden holds the default value on creation for the cache_ttl_overridden field.
usagelog
.
DefaultCacheTTLOverridden
=
usagelogDescCacheTTLOverridden
.
Default
.
(
bool
)
// usagelogDescCreatedAt is the schema descriptor for created_at field.
usagelogDescCreatedAt
:=
usagelogFields
[
29
]
.
Descriptor
()
usagelogDescCreatedAt
:=
usagelogFields
[
30
]
.
Descriptor
()
// usagelog.DefaultCreatedAt holds the default value on creation for the created_at field.
usagelog
.
DefaultCreatedAt
=
usagelogDescCreatedAt
.
Default
.
(
func
()
time
.
Time
)
userMixin
:=
schema
.
User
{}
.
Mixin
()
...
...
backend/ent/schema/usage_log.go
View file @
fad04ca9
...
...
@@ -119,6 +119,10 @@ func (UsageLog) Fields() []ent.Field {
Optional
()
.
Nillable
(),
// Cache TTL Override 标记(管理员强制替换了缓存 TTL 计费)
field
.
Bool
(
"cache_ttl_overridden"
)
.
Default
(
false
),
// 时间戳(只有 created_at,日志不可修改)
field
.
Time
(
"created_at"
)
.
Default
(
time
.
Now
)
.
...
...
backend/ent/usagelog.go
View file @
fad04ca9
...
...
@@ -80,6 +80,8 @@ type UsageLog struct {
ImageCount
int
`json:"image_count,omitempty"`
// ImageSize holds the value of the "image_size" field.
ImageSize
*
string
`json:"image_size,omitempty"`
// CacheTTLOverridden holds the value of the "cache_ttl_overridden" field.
CacheTTLOverridden
bool
`json:"cache_ttl_overridden,omitempty"`
// CreatedAt holds the value of the "created_at" field.
CreatedAt
time
.
Time
`json:"created_at,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
...
...
@@ -165,7 +167,7 @@ func (*UsageLog) scanValues(columns []string) ([]any, error) {
values
:=
make
([]
any
,
len
(
columns
))
for
i
:=
range
columns
{
switch
columns
[
i
]
{
case
usagelog
.
FieldStream
:
case
usagelog
.
FieldStream
,
usagelog
.
FieldCacheTTLOverridden
:
values
[
i
]
=
new
(
sql
.
NullBool
)
case
usagelog
.
FieldInputCost
,
usagelog
.
FieldOutputCost
,
usagelog
.
FieldCacheCreationCost
,
usagelog
.
FieldCacheReadCost
,
usagelog
.
FieldTotalCost
,
usagelog
.
FieldActualCost
,
usagelog
.
FieldRateMultiplier
,
usagelog
.
FieldAccountRateMultiplier
:
values
[
i
]
=
new
(
sql
.
NullFloat64
)
...
...
@@ -378,6 +380,12 @@ func (_m *UsageLog) assignValues(columns []string, values []any) error {
_m
.
ImageSize
=
new
(
string
)
*
_m
.
ImageSize
=
value
.
String
}
case
usagelog
.
FieldCacheTTLOverridden
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullBool
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field cache_ttl_overridden"
,
values
[
i
])
}
else
if
value
.
Valid
{
_m
.
CacheTTLOverridden
=
value
.
Bool
}
case
usagelog
.
FieldCreatedAt
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullTime
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field created_at"
,
values
[
i
])
...
...
@@ -548,6 +556,9 @@ func (_m *UsageLog) String() string {
builder
.
WriteString
(
*
v
)
}
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"cache_ttl_overridden="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
CacheTTLOverridden
))
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"created_at="
)
builder
.
WriteString
(
_m
.
CreatedAt
.
Format
(
time
.
ANSIC
))
builder
.
WriteByte
(
')'
)
...
...
backend/ent/usagelog/usagelog.go
View file @
fad04ca9
...
...
@@ -72,6 +72,8 @@ const (
FieldImageCount
=
"image_count"
// FieldImageSize holds the string denoting the image_size field in the database.
FieldImageSize
=
"image_size"
// FieldCacheTTLOverridden holds the string denoting the cache_ttl_overridden field in the database.
FieldCacheTTLOverridden
=
"cache_ttl_overridden"
// FieldCreatedAt holds the string denoting the created_at field in the database.
FieldCreatedAt
=
"created_at"
// EdgeUser holds the string denoting the user edge name in mutations.
...
...
@@ -155,6 +157,7 @@ var Columns = []string{
FieldIPAddress
,
FieldImageCount
,
FieldImageSize
,
FieldCacheTTLOverridden
,
FieldCreatedAt
,
}
...
...
@@ -211,6 +214,8 @@ var (
DefaultImageCount
int
// ImageSizeValidator is a validator for the "image_size" field. It is called by the builders before save.
ImageSizeValidator
func
(
string
)
error
// DefaultCacheTTLOverridden holds the default value on creation for the "cache_ttl_overridden" field.
DefaultCacheTTLOverridden
bool
// DefaultCreatedAt holds the default value on creation for the "created_at" field.
DefaultCreatedAt
func
()
time
.
Time
)
...
...
@@ -368,6 +373,11 @@ func ByImageSize(opts ...sql.OrderTermOption) OrderOption {
return
sql
.
OrderByField
(
FieldImageSize
,
opts
...
)
.
ToFunc
()
}
// ByCacheTTLOverridden orders the results by the cache_ttl_overridden field.
func
ByCacheTTLOverridden
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldCacheTTLOverridden
,
opts
...
)
.
ToFunc
()
}
// ByCreatedAt orders the results by the created_at field.
func
ByCreatedAt
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldCreatedAt
,
opts
...
)
.
ToFunc
()
...
...
backend/ent/usagelog/where.go
View file @
fad04ca9
...
...
@@ -200,6 +200,11 @@ func ImageSize(v string) predicate.UsageLog {
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldImageSize
,
v
))
}
// CacheTTLOverridden applies equality check predicate on the "cache_ttl_overridden" field. It's identical to CacheTTLOverriddenEQ.
func
CacheTTLOverridden
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCacheTTLOverridden
,
v
))
}
// CreatedAt applies equality check predicate on the "created_at" field. It's identical to CreatedAtEQ.
func
CreatedAt
(
v
time
.
Time
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
...
@@ -1440,6 +1445,16 @@ func ImageSizeContainsFold(v string) predicate.UsageLog {
return
predicate
.
UsageLog
(
sql
.
FieldContainsFold
(
FieldImageSize
,
v
))
}
// CacheTTLOverriddenEQ applies the EQ predicate on the "cache_ttl_overridden" field.
func
CacheTTLOverriddenEQ
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCacheTTLOverridden
,
v
))
}
// CacheTTLOverriddenNEQ applies the NEQ predicate on the "cache_ttl_overridden" field.
func
CacheTTLOverriddenNEQ
(
v
bool
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldNEQ
(
FieldCacheTTLOverridden
,
v
))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func
CreatedAtEQ
(
v
time
.
Time
)
predicate
.
UsageLog
{
return
predicate
.
UsageLog
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
...
backend/ent/usagelog_create.go
View file @
fad04ca9
...
...
@@ -393,6 +393,20 @@ func (_c *UsageLogCreate) SetNillableImageSize(v *string) *UsageLogCreate {
return
_c
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_c
*
UsageLogCreate
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogCreate
{
_c
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_c
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_c
*
UsageLogCreate
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogCreate
{
if
v
!=
nil
{
_c
.
SetCacheTTLOverridden
(
*
v
)
}
return
_c
}
// SetCreatedAt sets the "created_at" field.
func
(
_c
*
UsageLogCreate
)
SetCreatedAt
(
v
time
.
Time
)
*
UsageLogCreate
{
_c
.
mutation
.
SetCreatedAt
(
v
)
...
...
@@ -531,6 +545,10 @@ func (_c *UsageLogCreate) defaults() {
v
:=
usagelog
.
DefaultImageCount
_c
.
mutation
.
SetImageCount
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
!
ok
{
v
:=
usagelog
.
DefaultCacheTTLOverridden
_c
.
mutation
.
SetCacheTTLOverridden
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
v
:=
usagelog
.
DefaultCreatedAt
()
_c
.
mutation
.
SetCreatedAt
(
v
)
...
...
@@ -627,6 +645,9 @@ func (_c *UsageLogCreate) check() error {
return
&
ValidationError
{
Name
:
"image_size"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "UsageLog.image_size": %w`
,
err
)}
}
}
if
_
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
!
ok
{
return
&
ValidationError
{
Name
:
"cache_ttl_overridden"
,
err
:
errors
.
New
(
`ent: missing required field "UsageLog.cache_ttl_overridden"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
CreatedAt
();
!
ok
{
return
&
ValidationError
{
Name
:
"created_at"
,
err
:
errors
.
New
(
`ent: missing required field "UsageLog.created_at"`
)}
}
...
...
@@ -762,6 +783,10 @@ func (_c *UsageLogCreate) createSpec() (*UsageLog, *sqlgraph.CreateSpec) {
_spec
.
SetField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
,
value
)
_node
.
ImageSize
=
&
value
}
if
value
,
ok
:=
_c
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
_node
.
CacheTTLOverridden
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
CreatedAt
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCreatedAt
,
field
.
TypeTime
,
value
)
_node
.
CreatedAt
=
value
...
...
@@ -1407,6 +1432,18 @@ func (u *UsageLogUpsert) ClearImageSize() *UsageLogUpsert {
return
u
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsert
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsert
{
u
.
Set
(
usagelog
.
FieldCacheTTLOverridden
,
v
)
return
u
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsert
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsert
{
u
.
SetExcluded
(
usagelog
.
FieldCacheTTLOverridden
)
return
u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
...
...
@@ -2040,6 +2077,20 @@ func (u *UsageLogUpsertOne) ClearImageSize() *UsageLogUpsertOne {
})
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsertOne
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsertOne
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
SetCacheTTLOverridden
(
v
)
})
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsertOne
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsertOne
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
UpdateCacheTTLOverridden
()
})
}
// Exec executes the query.
func
(
u
*
UsageLogUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
if
len
(
u
.
create
.
conflict
)
==
0
{
...
...
@@ -2839,6 +2890,20 @@ func (u *UsageLogUpsertBulk) ClearImageSize() *UsageLogUpsertBulk {
})
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
u
*
UsageLogUpsertBulk
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
SetCacheTTLOverridden
(
v
)
})
}
// UpdateCacheTTLOverridden sets the "cache_ttl_overridden" field to the value that was provided on create.
func
(
u
*
UsageLogUpsertBulk
)
UpdateCacheTTLOverridden
()
*
UsageLogUpsertBulk
{
return
u
.
Update
(
func
(
s
*
UsageLogUpsert
)
{
s
.
UpdateCacheTTLOverridden
()
})
}
// Exec executes the query.
func
(
u
*
UsageLogUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
if
u
.
create
.
err
!=
nil
{
...
...
backend/ent/usagelog_update.go
View file @
fad04ca9
...
...
@@ -612,6 +612,20 @@ func (_u *UsageLogUpdate) ClearImageSize() *UsageLogUpdate {
return
_u
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_u
*
UsageLogUpdate
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpdate
{
_u
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_u
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_u
*
UsageLogUpdate
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogUpdate
{
if
v
!=
nil
{
_u
.
SetCacheTTLOverridden
(
*
v
)
}
return
_u
}
// SetUser sets the "user" edge to the User entity.
func
(
_u
*
UsageLogUpdate
)
SetUser
(
v
*
User
)
*
UsageLogUpdate
{
return
_u
.
SetUserID
(
v
.
ID
)
...
...
@@ -894,6 +908,9 @@ func (_u *UsageLogUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if
_u
.
mutation
.
ImageSizeCleared
()
{
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
}
if
_u
.
mutation
.
UserCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2O
,
...
...
@@ -1639,6 +1656,20 @@ func (_u *UsageLogUpdateOne) ClearImageSize() *UsageLogUpdateOne {
return
_u
}
// SetCacheTTLOverridden sets the "cache_ttl_overridden" field.
func
(
_u
*
UsageLogUpdateOne
)
SetCacheTTLOverridden
(
v
bool
)
*
UsageLogUpdateOne
{
_u
.
mutation
.
SetCacheTTLOverridden
(
v
)
return
_u
}
// SetNillableCacheTTLOverridden sets the "cache_ttl_overridden" field if the given value is not nil.
func
(
_u
*
UsageLogUpdateOne
)
SetNillableCacheTTLOverridden
(
v
*
bool
)
*
UsageLogUpdateOne
{
if
v
!=
nil
{
_u
.
SetCacheTTLOverridden
(
*
v
)
}
return
_u
}
// SetUser sets the "user" edge to the User entity.
func
(
_u
*
UsageLogUpdateOne
)
SetUser
(
v
*
User
)
*
UsageLogUpdateOne
{
return
_u
.
SetUserID
(
v
.
ID
)
...
...
@@ -1951,6 +1982,9 @@ func (_u *UsageLogUpdateOne) sqlSave(ctx context.Context) (_node *UsageLog, err
if
_u
.
mutation
.
ImageSizeCleared
()
{
_spec
.
ClearField
(
usagelog
.
FieldImageSize
,
field
.
TypeString
)
}
if
value
,
ok
:=
_u
.
mutation
.
CacheTTLOverridden
();
ok
{
_spec
.
SetField
(
usagelog
.
FieldCacheTTLOverridden
,
field
.
TypeBool
,
value
)
}
if
_u
.
mutation
.
UserCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
M2O
,
...
...
backend/internal/handler/dto/mappers.go
View file @
fad04ca9
...
...
@@ -211,6 +211,13 @@ func AccountFromServiceShallow(a *service.Account) *Account {
enabled
:=
true
out
.
EnableSessionIDMasking
=
&
enabled
}
// 缓存 TTL 强制替换
if
a
.
IsCacheTTLOverrideEnabled
()
{
enabled
:=
true
out
.
CacheTTLOverrideEnabled
=
&
enabled
target
:=
a
.
GetCacheTTLOverrideTarget
()
out
.
CacheTTLOverrideTarget
=
&
target
}
}
return
out
...
...
@@ -398,6 +405,7 @@ func usageLogFromServiceUser(l *service.UsageLog) UsageLog {
ImageCount
:
l
.
ImageCount
,
ImageSize
:
l
.
ImageSize
,
UserAgent
:
l
.
UserAgent
,
CacheTTLOverridden
:
l
.
CacheTTLOverridden
,
CreatedAt
:
l
.
CreatedAt
,
User
:
UserFromServiceShallow
(
l
.
User
),
APIKey
:
APIKeyFromService
(
l
.
APIKey
),
...
...
backend/internal/handler/dto/types.go
View file @
fad04ca9
...
...
@@ -150,6 +150,11 @@ type Account struct {
// 从 extra 字段提取,方便前端显示和编辑
EnableSessionIDMasking
*
bool
`json:"session_id_masking_enabled,omitempty"`
// 缓存 TTL 强制替换(仅 Anthropic OAuth/SetupToken 账号有效)
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型计费
CacheTTLOverrideEnabled
*
bool
`json:"cache_ttl_override_enabled,omitempty"`
CacheTTLOverrideTarget
*
string
`json:"cache_ttl_override_target,omitempty"`
Proxy
*
Proxy
`json:"proxy,omitempty"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
...
...
@@ -273,6 +278,9 @@ type UsageLog struct {
// User-Agent
UserAgent
*
string
`json:"user_agent"`
// Cache TTL Override 标记
CacheTTLOverridden
bool
`json:"cache_ttl_overridden"`
CreatedAt
time
.
Time
`json:"created_at"`
User
*
User
`json:"user,omitempty"`
...
...
backend/internal/pkg/claude/constants.go
View file @
fad04ca9
...
...
@@ -10,6 +10,7 @@ const (
BetaInterleavedThinking
=
"interleaved-thinking-2025-05-14"
BetaFineGrainedToolStreaming
=
"fine-grained-tool-streaming-2025-05-14"
BetaTokenCounting
=
"token-counting-2024-11-01"
BetaContext1M
=
"context-1m-2025-08-07"
)
// DefaultBetaHeader Claude Code 客户端默认的 anthropic-beta header
...
...
@@ -77,6 +78,12 @@ var DefaultModels = []Model{
DisplayName
:
"Claude Opus 4.6"
,
CreatedAt
:
"2026-02-06T00:00:00Z"
,
},
{
ID
:
"claude-sonnet-4-6"
,
Type
:
"model"
,
DisplayName
:
"Claude Sonnet 4.6"
,
CreatedAt
:
"2026-02-18T00:00:00Z"
,
},
{
ID
:
"claude-sonnet-4-5-20250929"
,
Type
:
"model"
,
...
...
backend/internal/repository/usage_log_repo.go
View file @
fad04ca9
...
...
@@ -22,7 +22,7 @@ import (
"github.com/lib/pq"
)
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, reasoning_effort, created_at"
const
usageLogSelectColumns
=
"id, user_id, api_key_id, account_id, request_id, model, group_id, subscription_id, input_tokens, output_tokens, cache_creation_tokens, cache_read_tokens, cache_creation_5m_tokens, cache_creation_1h_tokens, input_cost, output_cost, cache_creation_cost, cache_read_cost, total_cost, actual_cost, rate_multiplier, account_rate_multiplier, billing_type, stream, duration_ms, first_token_ms, user_agent, ip_address, image_count, image_size, reasoning_effort,
cache_ttl_overridden,
created_at"
// dateFormatWhitelist 将 granularity 参数映射为 PostgreSQL TO_CHAR 格式字符串,防止外部输入直接拼入 SQL
var
dateFormatWhitelist
=
map
[
string
]
string
{
...
...
@@ -131,6 +131,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
image_count,
image_size,
reasoning_effort,
cache_ttl_overridden,
created_at
) VALUES (
$1, $2, $3, $4, $5,
...
...
@@ -138,7 +139,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
$8, $9, $10, $11,
$12, $13,
$14, $15, $16, $17, $18, $19,
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31
$20, $21, $22, $23, $24, $25, $26, $27, $28, $29, $30, $31
, $32
)
ON CONFLICT (request_id, api_key_id) DO NOTHING
RETURNING id, created_at
...
...
@@ -189,6 +190,7 @@ func (r *usageLogRepository) Create(ctx context.Context, log *service.UsageLog)
log
.
ImageCount
,
imageSize
,
reasoningEffort
,
log
.
CacheTTLOverridden
,
createdAt
,
}
if
err
:=
scanSingleRow
(
ctx
,
sqlq
,
query
,
args
,
&
log
.
ID
,
&
log
.
CreatedAt
);
err
!=
nil
{
...
...
@@ -2217,6 +2219,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
imageCount
int
imageSize
sql
.
NullString
reasoningEffort
sql
.
NullString
cacheTTLOverridden
bool
createdAt
time
.
Time
)
...
...
@@ -2252,6 +2255,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
&
imageCount
,
&
imageSize
,
&
reasoningEffort
,
&
cacheTTLOverridden
,
&
createdAt
,
);
err
!=
nil
{
return
nil
,
err
...
...
@@ -2280,6 +2284,7 @@ func scanUsageLog(scanner interface{ Scan(...any) error }) (*service.UsageLog, e
BillingType
:
int8
(
billingType
),
Stream
:
stream
,
ImageCount
:
imageCount
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
createdAt
,
}
...
...
backend/internal/server/api_contract_test.go
View file @
fad04ca9
...
...
@@ -401,6 +401,7 @@ func TestAPIContracts(t *testing.T) {
"first_token_ms": 50,
"image_count": 0,
"image_size": null,
"cache_ttl_overridden": false,
"created_at": "2025-01-02T03:04:05Z",
"user_agent": null
}
...
...
backend/internal/server/middleware/cors.go
View file @
fad04ca9
...
...
@@ -70,7 +70,15 @@ func CORS(cfg config.CORSConfig) gin.HandlerFunc {
}
}
c
.
Writer
.
Header
()
.
Set
(
"Access-Control-Allow-Headers"
,
"Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With, X-API-Key"
)
allowHeaders
:=
[]
string
{
"Content-Type"
,
"Content-Length"
,
"Accept-Encoding"
,
"X-CSRF-Token"
,
"Authorization"
,
"accept"
,
"origin"
,
"Cache-Control"
,
"X-Requested-With"
,
"X-API-Key"
}
// openai node sdk
openAIProperties
:=
[]
string
{
"lang"
,
"package-version"
,
"os"
,
"arch"
,
"retry-count"
,
"runtime"
,
"runtime-version"
,
"async"
,
"helper-method"
,
"poll-helper"
,
"custom-poll-interval"
,
"timeout"
}
for
_
,
prop
:=
range
openAIProperties
{
allowHeaders
=
append
(
allowHeaders
,
"x-stainless-"
+
prop
)
}
c
.
Writer
.
Header
()
.
Set
(
"Access-Control-Allow-Headers"
,
strings
.
Join
(
allowHeaders
,
", "
))
c
.
Writer
.
Header
()
.
Set
(
"Access-Control-Allow-Methods"
,
"POST, OPTIONS, GET, PUT, DELETE, PATCH"
)
c
.
Writer
.
Header
()
.
Set
(
"Access-Control-Max-Age"
,
"86400"
)
...
...
backend/internal/service/account.go
View file @
fad04ca9
...
...
@@ -752,6 +752,38 @@ func (a *Account) IsSessionIDMaskingEnabled() bool {
return
false
}
// IsCacheTTLOverrideEnabled 检查是否启用缓存 TTL 强制替换
// 仅适用于 Anthropic OAuth/SetupToken 类型账号
// 启用后将所有 cache creation tokens 归入指定的 TTL 类型(5m 或 1h)
func
(
a
*
Account
)
IsCacheTTLOverrideEnabled
()
bool
{
if
!
a
.
IsAnthropicOAuthOrSetupToken
()
{
return
false
}
if
a
.
Extra
==
nil
{
return
false
}
if
v
,
ok
:=
a
.
Extra
[
"cache_ttl_override_enabled"
];
ok
{
if
enabled
,
ok
:=
v
.
(
bool
);
ok
{
return
enabled
}
}
return
false
}
// GetCacheTTLOverrideTarget 获取缓存 TTL 强制替换的目标类型
// 返回 "5m" 或 "1h",默认 "5m"
func
(
a
*
Account
)
GetCacheTTLOverrideTarget
()
string
{
if
a
.
Extra
==
nil
{
return
"5m"
}
if
v
,
ok
:=
a
.
Extra
[
"cache_ttl_override_target"
];
ok
{
if
target
,
ok
:=
v
.
(
string
);
ok
&&
(
target
==
"5m"
||
target
==
"1h"
)
{
return
target
}
}
return
"5m"
}
// GetWindowCostLimit 获取 5h 窗口费用阈值(美元)
// 返回 0 表示未启用
func
(
a
*
Account
)
GetWindowCostLimit
()
float64
{
...
...
backend/internal/service/gateway_beta_test.go
View file @
fad04ca9
...
...
@@ -21,3 +21,72 @@ func TestMergeAnthropicBeta_EmptyIncoming(t *testing.T) {
)
require
.
Equal
(
t
,
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
got
)
}
func
TestStripBetaToken
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
header
string
token
string
want
string
}{
{
name
:
"token in middle"
,
header
:
"oauth-2025-04-20,context-1m-2025-08-07,interleaved-thinking-2025-05-14"
,
token
:
"context-1m-2025-08-07"
,
want
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
},
{
name
:
"token at start"
,
header
:
"context-1m-2025-08-07,oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
token
:
"context-1m-2025-08-07"
,
want
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
},
{
name
:
"token at end"
,
header
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14,context-1m-2025-08-07"
,
token
:
"context-1m-2025-08-07"
,
want
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
},
{
name
:
"token not present"
,
header
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
token
:
"context-1m-2025-08-07"
,
want
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
},
{
name
:
"empty header"
,
header
:
""
,
token
:
"context-1m-2025-08-07"
,
want
:
""
,
},
{
name
:
"with spaces"
,
header
:
"oauth-2025-04-20, context-1m-2025-08-07 , interleaved-thinking-2025-05-14"
,
token
:
"context-1m-2025-08-07"
,
want
:
"oauth-2025-04-20,interleaved-thinking-2025-05-14"
,
},
{
name
:
"only token"
,
header
:
"context-1m-2025-08-07"
,
token
:
"context-1m-2025-08-07"
,
want
:
""
,
},
}
for
_
,
tt
:=
range
tests
{
t
.
Run
(
tt
.
name
,
func
(
t
*
testing
.
T
)
{
got
:=
stripBetaToken
(
tt
.
header
,
tt
.
token
)
require
.
Equal
(
t
,
tt
.
want
,
got
)
})
}
}
func
TestMergeAnthropicBetaDropping_Context1M
(
t
*
testing
.
T
)
{
required
:=
[]
string
{
"oauth-2025-04-20"
,
"interleaved-thinking-2025-05-14"
}
incoming
:=
"context-1m-2025-08-07,foo-beta,oauth-2025-04-20"
drop
:=
map
[
string
]
struct
{}{
"context-1m-2025-08-07"
:
{}}
got
:=
mergeAnthropicBetaDropping
(
required
,
incoming
,
drop
)
require
.
Equal
(
t
,
"oauth-2025-04-20,interleaved-thinking-2025-05-14,foo-beta"
,
got
)
require
.
NotContains
(
t
,
got
,
"context-1m-2025-08-07"
)
}
backend/internal/service/gateway_service.go
View file @
fad04ca9
...
...
@@ -3553,12 +3553,12 @@ func (s *GatewayService) buildUpstreamRequest(ctx context.Context, c *gin.Contex
// messages requests typically use only oauth + interleaved-thinking.
// Also drop claude-code beta if a downstream client added it.
requiredBetas
:=
[]
string
{
claude
.
BetaOAuth
,
claude
.
BetaInterleavedThinking
}
drop
:=
map
[
string
]
struct
{}{
claude
.
BetaClaudeCode
:
{}}
drop
:=
map
[
string
]
struct
{}{
claude
.
BetaClaudeCode
:
{},
claude
.
BetaContext1M
:
{}}
req
.
Header
.
Set
(
"anthropic-beta"
,
mergeAnthropicBetaDropping
(
requiredBetas
,
incomingBeta
,
drop
))
}
else
{
// Claude Code 客户端:尽量透传原始 header,仅补齐 oauth beta
clientBetaHeader
:=
req
.
Header
.
Get
(
"anthropic-beta"
)
req
.
Header
.
Set
(
"anthropic-beta"
,
s
.
getBetaHeader
(
modelID
,
clientBetaHeader
))
req
.
Header
.
Set
(
"anthropic-beta"
,
stripBetaToken
(
s
.
getBetaHeader
(
modelID
,
clientBetaHeader
)
,
claude
.
BetaContext1M
)
)
}
}
else
if
s
.
cfg
!=
nil
&&
s
.
cfg
.
Gateway
.
InjectBetaForAPIKey
&&
req
.
Header
.
Get
(
"anthropic-beta"
)
==
""
{
// API-key:仅在请求显式使用 beta 特性且客户端未提供时,按需补齐(默认关闭)
...
...
@@ -3712,6 +3712,23 @@ func mergeAnthropicBetaDropping(required []string, incoming string, drop map[str
return
strings
.
Join
(
out
,
","
)
}
// stripBetaToken removes a single beta token from a comma-separated header value.
// It short-circuits when the token is not present to avoid unnecessary allocations.
func
stripBetaToken
(
header
,
token
string
)
string
{
if
!
strings
.
Contains
(
header
,
token
)
{
return
header
}
out
:=
make
([]
string
,
0
,
8
)
for
_
,
p
:=
range
strings
.
Split
(
header
,
","
)
{
p
=
strings
.
TrimSpace
(
p
)
if
p
==
""
||
p
==
token
{
continue
}
out
=
append
(
out
,
p
)
}
return
strings
.
Join
(
out
,
","
)
}
// applyClaudeCodeMimicHeaders forces "Claude Code-like" request headers.
// This mirrors opencode-anthropic-auth behavior: do not trust downstream
// headers when using Claude Code-scoped OAuth credentials.
...
...
@@ -4278,6 +4295,23 @@ func (s *GatewayService) handleStreamingResponse(ctx context.Context, resp *http
}
}
// Cache TTL Override: 重写 SSE 事件中的 cache_creation 分类
if
account
.
IsCacheTTLOverrideEnabled
()
{
overrideTarget
:=
account
.
GetCacheTTLOverrideTarget
()
if
eventType
==
"message_start"
{
if
msg
,
ok
:=
event
[
"message"
]
.
(
map
[
string
]
any
);
ok
{
if
u
,
ok
:=
msg
[
"usage"
]
.
(
map
[
string
]
any
);
ok
{
rewriteCacheCreationJSON
(
u
,
overrideTarget
)
}
}
}
if
eventType
==
"message_delta"
{
if
u
,
ok
:=
event
[
"usage"
]
.
(
map
[
string
]
any
);
ok
{
rewriteCacheCreationJSON
(
u
,
overrideTarget
)
}
}
}
if
needModelReplace
{
if
msg
,
ok
:=
event
[
"message"
]
.
(
map
[
string
]
any
);
ok
{
if
model
,
ok
:=
msg
[
"model"
]
.
(
string
);
ok
&&
model
==
mappedModel
{
...
...
@@ -4452,6 +4486,58 @@ func (s *GatewayService) parseSSEUsage(data string, usage *ClaudeUsage) {
}
}
// applyCacheTTLOverride 将所有 cache creation tokens 归入指定的 TTL 类型。
// target 为 "5m" 或 "1h"。返回 true 表示发生了变更。
func
applyCacheTTLOverride
(
usage
*
ClaudeUsage
,
target
string
)
bool
{
// Fallback: 如果只有聚合字段但无 5m/1h 明细,将聚合字段归入 5m 默认类别
if
usage
.
CacheCreation5mTokens
==
0
&&
usage
.
CacheCreation1hTokens
==
0
&&
usage
.
CacheCreationInputTokens
>
0
{
usage
.
CacheCreation5mTokens
=
usage
.
CacheCreationInputTokens
}
total
:=
usage
.
CacheCreation5mTokens
+
usage
.
CacheCreation1hTokens
if
total
==
0
{
return
false
}
switch
target
{
case
"1h"
:
if
usage
.
CacheCreation1hTokens
==
total
{
return
false
// 已经全是 1h
}
usage
.
CacheCreation1hTokens
=
total
usage
.
CacheCreation5mTokens
=
0
default
:
// "5m"
if
usage
.
CacheCreation5mTokens
==
total
{
return
false
// 已经全是 5m
}
usage
.
CacheCreation5mTokens
=
total
usage
.
CacheCreation1hTokens
=
0
}
return
true
}
// rewriteCacheCreationJSON 在 JSON usage 对象中重写 cache_creation 嵌套对象的 TTL 分类。
// usageObj 是 usage JSON 对象(map[string]any)。
func
rewriteCacheCreationJSON
(
usageObj
map
[
string
]
any
,
target
string
)
{
ccObj
,
ok
:=
usageObj
[
"cache_creation"
]
.
(
map
[
string
]
any
)
if
!
ok
{
return
}
v5m
,
_
:=
ccObj
[
"ephemeral_5m_input_tokens"
]
.
(
float64
)
v1h
,
_
:=
ccObj
[
"ephemeral_1h_input_tokens"
]
.
(
float64
)
total
:=
v5m
+
v1h
if
total
==
0
{
return
}
switch
target
{
case
"1h"
:
ccObj
[
"ephemeral_1h_input_tokens"
]
=
total
ccObj
[
"ephemeral_5m_input_tokens"
]
=
float64
(
0
)
default
:
// "5m"
ccObj
[
"ephemeral_5m_input_tokens"
]
=
total
ccObj
[
"ephemeral_1h_input_tokens"
]
=
float64
(
0
)
}
}
func
(
s
*
GatewayService
)
handleNonStreamingResponse
(
ctx
context
.
Context
,
resp
*
http
.
Response
,
c
*
gin
.
Context
,
account
*
Account
,
originalModel
,
mappedModel
string
)
(
*
ClaudeUsage
,
error
)
{
// 更新5h窗口状态
s
.
rateLimitService
.
UpdateSessionWindow
(
ctx
,
account
,
resp
.
Header
)
...
...
@@ -4488,6 +4574,20 @@ func (s *GatewayService) handleNonStreamingResponse(ctx context.Context, resp *h
}
}
// Cache TTL Override: 重写 non-streaming 响应中的 cache_creation 分类
if
account
.
IsCacheTTLOverrideEnabled
()
{
overrideTarget
:=
account
.
GetCacheTTLOverrideTarget
()
if
applyCacheTTLOverride
(
&
response
.
Usage
,
overrideTarget
)
{
// 同步更新 body JSON 中的嵌套 cache_creation 对象
if
newBody
,
err
:=
sjson
.
SetBytes
(
body
,
"usage.cache_creation.ephemeral_5m_input_tokens"
,
response
.
Usage
.
CacheCreation5mTokens
);
err
==
nil
{
body
=
newBody
}
if
newBody
,
err
:=
sjson
.
SetBytes
(
body
,
"usage.cache_creation.ephemeral_1h_input_tokens"
,
response
.
Usage
.
CacheCreation1hTokens
);
err
==
nil
{
body
=
newBody
}
}
}
// 如果有模型映射,替换响应中的model字段
if
originalModel
!=
mappedModel
{
body
=
s
.
replaceModelInResponseBody
(
body
,
mappedModel
,
originalModel
)
...
...
@@ -4556,6 +4656,13 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
result
.
Usage
.
InputTokens
=
0
}
// Cache TTL Override: 确保计费时 token 分类与账号设置一致
cacheTTLOverridden
:=
false
if
account
.
IsCacheTTLOverrideEnabled
()
{
applyCacheTTLOverride
(
&
result
.
Usage
,
account
.
GetCacheTTLOverrideTarget
())
cacheTTLOverridden
=
(
result
.
Usage
.
CacheCreation5mTokens
+
result
.
Usage
.
CacheCreation1hTokens
)
>
0
}
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
...
...
@@ -4641,6 +4748,7 @@ func (s *GatewayService) RecordUsage(ctx context.Context, input *RecordUsageInpu
FirstTokenMs
:
result
.
FirstTokenMs
,
ImageCount
:
result
.
ImageCount
,
ImageSize
:
imageSize
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
time
.
Now
(),
}
...
...
@@ -4741,6 +4849,13 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
result
.
Usage
.
InputTokens
=
0
}
// Cache TTL Override: 确保计费时 token 分类与账号设置一致
cacheTTLOverridden
:=
false
if
account
.
IsCacheTTLOverrideEnabled
()
{
applyCacheTTLOverride
(
&
result
.
Usage
,
account
.
GetCacheTTLOverrideTarget
())
cacheTTLOverridden
=
(
result
.
Usage
.
CacheCreation5mTokens
+
result
.
Usage
.
CacheCreation1hTokens
)
>
0
}
// 获取费率倍数(优先级:用户专属 > 分组默认 > 系统默认)
multiplier
:=
s
.
cfg
.
Default
.
RateMultiplier
if
apiKey
.
GroupID
!=
nil
&&
apiKey
.
Group
!=
nil
{
...
...
@@ -4826,6 +4941,7 @@ func (s *GatewayService) RecordUsageWithLongContext(ctx context.Context, input *
FirstTokenMs
:
result
.
FirstTokenMs
,
ImageCount
:
result
.
ImageCount
,
ImageSize
:
imageSize
,
CacheTTLOverridden
:
cacheTTLOverridden
,
CreatedAt
:
time
.
Now
(),
}
...
...
@@ -5131,7 +5247,8 @@ func (s *GatewayService) buildCountTokensRequest(ctx context.Context, c *gin.Con
incomingBeta
:=
req
.
Header
.
Get
(
"anthropic-beta"
)
requiredBetas
:=
[]
string
{
claude
.
BetaClaudeCode
,
claude
.
BetaOAuth
,
claude
.
BetaInterleavedThinking
,
claude
.
BetaTokenCounting
}
req
.
Header
.
Set
(
"anthropic-beta"
,
mergeAnthropicBeta
(
requiredBetas
,
incomingBeta
))
drop
:=
map
[
string
]
struct
{}{
claude
.
BetaContext1M
:
{}}
req
.
Header
.
Set
(
"anthropic-beta"
,
mergeAnthropicBetaDropping
(
requiredBetas
,
incomingBeta
,
drop
))
}
else
{
clientBetaHeader
:=
req
.
Header
.
Get
(
"anthropic-beta"
)
if
clientBetaHeader
==
""
{
...
...
@@ -5141,7 +5258,7 @@ func (s *GatewayService) buildCountTokensRequest(ctx context.Context, c *gin.Con
if
!
strings
.
Contains
(
beta
,
claude
.
BetaTokenCounting
)
{
beta
=
beta
+
","
+
claude
.
BetaTokenCounting
}
req
.
Header
.
Set
(
"anthropic-beta"
,
beta
)
req
.
Header
.
Set
(
"anthropic-beta"
,
stripBetaToken
(
beta
,
claude
.
BetaContext1M
)
)
}
}
}
else
if
s
.
cfg
!=
nil
&&
s
.
cfg
.
Gateway
.
InjectBetaForAPIKey
&&
req
.
Header
.
Get
(
"anthropic-beta"
)
==
""
{
...
...
backend/internal/service/openai_codex_transform.go
View file @
fad04ca9
...
...
@@ -94,13 +94,19 @@ func applyCodexOAuthTransform(reqBody map[string]any, isCodexCLI bool) codexTran
result
.
Modified
=
true
}
if
_
,
ok
:=
reqBody
[
"max_output_tokens"
];
ok
{
delete
(
reqBody
,
"max_output_tokens"
)
result
.
Modified
=
true
}
if
_
,
ok
:=
reqBody
[
"max_completion_tokens"
];
ok
{
delete
(
reqBody
,
"max_completion_tokens"
)
result
.
Modified
=
true
// Strip parameters unsupported by codex models via the Responses API.
for
_
,
key
:=
range
[]
string
{
"max_output_tokens"
,
"max_completion_tokens"
,
"temperature"
,
"top_p"
,
"frequency_penalty"
,
"presence_penalty"
,
}
{
if
_
,
ok
:=
reqBody
[
key
];
ok
{
delete
(
reqBody
,
key
)
result
.
Modified
=
true
}
}
if
normalizeCodexTools
(
reqBody
)
{
...
...
Prev
1
2
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment