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
92d35409
"backend/cmd/vscode:/vscode.git/clone" did not exist on "0d69c0cd643bab82bd011682407030955f2389a7"
Commit
92d35409
authored
Mar 07, 2026
by
shaw
Browse files
feat: 为openai分组增加messages调度开关和默认映射模型
parent
351a08f8
Changes
27
Hide whitespace changes
Inline
Side-by-side
backend/ent/group.go
View file @
92d35409
...
...
@@ -78,6 +78,10 @@ type Group struct {
SupportedModelScopes
[]
string
`json:"supported_model_scopes,omitempty"`
// 分组显示排序,数值越小越靠前
SortOrder
int
`json:"sort_order,omitempty"`
// 是否允许 /v1/messages 调度到此 OpenAI 分组
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch,omitempty"`
// 默认映射模型 ID,当账号级映射找不到时使用此值
DefaultMappedModel
string
`json:"default_mapped_model,omitempty"`
// Edges holds the relations/edges for other nodes in the graph.
// The values are being populated by the GroupQuery when eager-loading is set.
Edges
GroupEdges
`json:"edges"`
...
...
@@ -186,13 +190,13 @@ func (*Group) scanValues(columns []string) ([]any, error) {
switch
columns
[
i
]
{
case
group
.
FieldModelRouting
,
group
.
FieldSupportedModelScopes
:
values
[
i
]
=
new
([]
byte
)
case
group
.
FieldIsExclusive
,
group
.
FieldClaudeCodeOnly
,
group
.
FieldModelRoutingEnabled
,
group
.
FieldMcpXMLInject
:
case
group
.
FieldIsExclusive
,
group
.
FieldClaudeCodeOnly
,
group
.
FieldModelRoutingEnabled
,
group
.
FieldMcpXMLInject
,
group
.
FieldAllowMessagesDispatch
:
values
[
i
]
=
new
(
sql
.
NullBool
)
case
group
.
FieldRateMultiplier
,
group
.
FieldDailyLimitUsd
,
group
.
FieldWeeklyLimitUsd
,
group
.
FieldMonthlyLimitUsd
,
group
.
FieldImagePrice1k
,
group
.
FieldImagePrice2k
,
group
.
FieldImagePrice4k
,
group
.
FieldSoraImagePrice360
,
group
.
FieldSoraImagePrice540
,
group
.
FieldSoraVideoPricePerRequest
,
group
.
FieldSoraVideoPricePerRequestHd
:
values
[
i
]
=
new
(
sql
.
NullFloat64
)
case
group
.
FieldID
,
group
.
FieldDefaultValidityDays
,
group
.
FieldSoraStorageQuotaBytes
,
group
.
FieldFallbackGroupID
,
group
.
FieldFallbackGroupIDOnInvalidRequest
,
group
.
FieldSortOrder
:
values
[
i
]
=
new
(
sql
.
NullInt64
)
case
group
.
FieldName
,
group
.
FieldDescription
,
group
.
FieldStatus
,
group
.
FieldPlatform
,
group
.
FieldSubscriptionType
:
case
group
.
FieldName
,
group
.
FieldDescription
,
group
.
FieldStatus
,
group
.
FieldPlatform
,
group
.
FieldSubscriptionType
,
group
.
FieldDefaultMappedModel
:
values
[
i
]
=
new
(
sql
.
NullString
)
case
group
.
FieldCreatedAt
,
group
.
FieldUpdatedAt
,
group
.
FieldDeletedAt
:
values
[
i
]
=
new
(
sql
.
NullTime
)
...
...
@@ -415,6 +419,18 @@ func (_m *Group) assignValues(columns []string, values []any) error {
}
else
if
value
.
Valid
{
_m
.
SortOrder
=
int
(
value
.
Int64
)
}
case
group
.
FieldAllowMessagesDispatch
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullBool
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field allow_messages_dispatch"
,
values
[
i
])
}
else
if
value
.
Valid
{
_m
.
AllowMessagesDispatch
=
value
.
Bool
}
case
group
.
FieldDefaultMappedModel
:
if
value
,
ok
:=
values
[
i
]
.
(
*
sql
.
NullString
);
!
ok
{
return
fmt
.
Errorf
(
"unexpected type %T for field default_mapped_model"
,
values
[
i
])
}
else
if
value
.
Valid
{
_m
.
DefaultMappedModel
=
value
.
String
}
default
:
_m
.
selectValues
.
Set
(
columns
[
i
],
values
[
i
])
}
...
...
@@ -608,6 +624,12 @@ func (_m *Group) String() string {
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"sort_order="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
SortOrder
))
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"allow_messages_dispatch="
)
builder
.
WriteString
(
fmt
.
Sprintf
(
"%v"
,
_m
.
AllowMessagesDispatch
))
builder
.
WriteString
(
", "
)
builder
.
WriteString
(
"default_mapped_model="
)
builder
.
WriteString
(
_m
.
DefaultMappedModel
)
builder
.
WriteByte
(
')'
)
return
builder
.
String
()
}
...
...
backend/ent/group/group.go
View file @
92d35409
...
...
@@ -75,6 +75,10 @@ const (
FieldSupportedModelScopes
=
"supported_model_scopes"
// FieldSortOrder holds the string denoting the sort_order field in the database.
FieldSortOrder
=
"sort_order"
// FieldAllowMessagesDispatch holds the string denoting the allow_messages_dispatch field in the database.
FieldAllowMessagesDispatch
=
"allow_messages_dispatch"
// FieldDefaultMappedModel holds the string denoting the default_mapped_model field in the database.
FieldDefaultMappedModel
=
"default_mapped_model"
// EdgeAPIKeys holds the string denoting the api_keys edge name in mutations.
EdgeAPIKeys
=
"api_keys"
// EdgeRedeemCodes holds the string denoting the redeem_codes edge name in mutations.
...
...
@@ -180,6 +184,8 @@ var Columns = []string{
FieldMcpXMLInject
,
FieldSupportedModelScopes
,
FieldSortOrder
,
FieldAllowMessagesDispatch
,
FieldDefaultMappedModel
,
}
var
(
...
...
@@ -247,6 +253,12 @@ var (
DefaultSupportedModelScopes
[]
string
// DefaultSortOrder holds the default value on creation for the "sort_order" field.
DefaultSortOrder
int
// DefaultAllowMessagesDispatch holds the default value on creation for the "allow_messages_dispatch" field.
DefaultAllowMessagesDispatch
bool
// DefaultDefaultMappedModel holds the default value on creation for the "default_mapped_model" field.
DefaultDefaultMappedModel
string
// DefaultMappedModelValidator is a validator for the "default_mapped_model" field. It is called by the builders before save.
DefaultMappedModelValidator
func
(
string
)
error
)
// OrderOption defines the ordering options for the Group queries.
...
...
@@ -397,6 +409,16 @@ func BySortOrder(opts ...sql.OrderTermOption) OrderOption {
return
sql
.
OrderByField
(
FieldSortOrder
,
opts
...
)
.
ToFunc
()
}
// ByAllowMessagesDispatch orders the results by the allow_messages_dispatch field.
func
ByAllowMessagesDispatch
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldAllowMessagesDispatch
,
opts
...
)
.
ToFunc
()
}
// ByDefaultMappedModel orders the results by the default_mapped_model field.
func
ByDefaultMappedModel
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
sql
.
OrderByField
(
FieldDefaultMappedModel
,
opts
...
)
.
ToFunc
()
}
// ByAPIKeysCount orders the results by api_keys count.
func
ByAPIKeysCount
(
opts
...
sql
.
OrderTermOption
)
OrderOption
{
return
func
(
s
*
sql
.
Selector
)
{
...
...
backend/ent/group/where.go
View file @
92d35409
...
...
@@ -195,6 +195,16 @@ func SortOrder(v int) predicate.Group {
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldSortOrder
,
v
))
}
// AllowMessagesDispatch applies equality check predicate on the "allow_messages_dispatch" field. It's identical to AllowMessagesDispatchEQ.
func
AllowMessagesDispatch
(
v
bool
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldAllowMessagesDispatch
,
v
))
}
// DefaultMappedModel applies equality check predicate on the "default_mapped_model" field. It's identical to DefaultMappedModelEQ.
func
DefaultMappedModel
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldDefaultMappedModel
,
v
))
}
// CreatedAtEQ applies the EQ predicate on the "created_at" field.
func
CreatedAtEQ
(
v
time
.
Time
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldCreatedAt
,
v
))
...
...
@@ -1470,6 +1480,81 @@ func SortOrderLTE(v int) predicate.Group {
return
predicate
.
Group
(
sql
.
FieldLTE
(
FieldSortOrder
,
v
))
}
// AllowMessagesDispatchEQ applies the EQ predicate on the "allow_messages_dispatch" field.
func
AllowMessagesDispatchEQ
(
v
bool
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldAllowMessagesDispatch
,
v
))
}
// AllowMessagesDispatchNEQ applies the NEQ predicate on the "allow_messages_dispatch" field.
func
AllowMessagesDispatchNEQ
(
v
bool
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldNEQ
(
FieldAllowMessagesDispatch
,
v
))
}
// DefaultMappedModelEQ applies the EQ predicate on the "default_mapped_model" field.
func
DefaultMappedModelEQ
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEQ
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelNEQ applies the NEQ predicate on the "default_mapped_model" field.
func
DefaultMappedModelNEQ
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldNEQ
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelIn applies the In predicate on the "default_mapped_model" field.
func
DefaultMappedModelIn
(
vs
...
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldIn
(
FieldDefaultMappedModel
,
vs
...
))
}
// DefaultMappedModelNotIn applies the NotIn predicate on the "default_mapped_model" field.
func
DefaultMappedModelNotIn
(
vs
...
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldNotIn
(
FieldDefaultMappedModel
,
vs
...
))
}
// DefaultMappedModelGT applies the GT predicate on the "default_mapped_model" field.
func
DefaultMappedModelGT
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldGT
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelGTE applies the GTE predicate on the "default_mapped_model" field.
func
DefaultMappedModelGTE
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldGTE
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelLT applies the LT predicate on the "default_mapped_model" field.
func
DefaultMappedModelLT
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldLT
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelLTE applies the LTE predicate on the "default_mapped_model" field.
func
DefaultMappedModelLTE
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldLTE
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelContains applies the Contains predicate on the "default_mapped_model" field.
func
DefaultMappedModelContains
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldContains
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelHasPrefix applies the HasPrefix predicate on the "default_mapped_model" field.
func
DefaultMappedModelHasPrefix
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldHasPrefix
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelHasSuffix applies the HasSuffix predicate on the "default_mapped_model" field.
func
DefaultMappedModelHasSuffix
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldHasSuffix
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelEqualFold applies the EqualFold predicate on the "default_mapped_model" field.
func
DefaultMappedModelEqualFold
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldEqualFold
(
FieldDefaultMappedModel
,
v
))
}
// DefaultMappedModelContainsFold applies the ContainsFold predicate on the "default_mapped_model" field.
func
DefaultMappedModelContainsFold
(
v
string
)
predicate
.
Group
{
return
predicate
.
Group
(
sql
.
FieldContainsFold
(
FieldDefaultMappedModel
,
v
))
}
// HasAPIKeys applies the HasEdge predicate on the "api_keys" edge.
func
HasAPIKeys
()
predicate
.
Group
{
return
predicate
.
Group
(
func
(
s
*
sql
.
Selector
)
{
...
...
backend/ent/group_create.go
View file @
92d35409
...
...
@@ -424,6 +424,34 @@ func (_c *GroupCreate) SetNillableSortOrder(v *int) *GroupCreate {
return
_c
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
_c
*
GroupCreate
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupCreate
{
_c
.
mutation
.
SetAllowMessagesDispatch
(
v
)
return
_c
}
// SetNillableAllowMessagesDispatch sets the "allow_messages_dispatch" field if the given value is not nil.
func
(
_c
*
GroupCreate
)
SetNillableAllowMessagesDispatch
(
v
*
bool
)
*
GroupCreate
{
if
v
!=
nil
{
_c
.
SetAllowMessagesDispatch
(
*
v
)
}
return
_c
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
_c
*
GroupCreate
)
SetDefaultMappedModel
(
v
string
)
*
GroupCreate
{
_c
.
mutation
.
SetDefaultMappedModel
(
v
)
return
_c
}
// SetNillableDefaultMappedModel sets the "default_mapped_model" field if the given value is not nil.
func
(
_c
*
GroupCreate
)
SetNillableDefaultMappedModel
(
v
*
string
)
*
GroupCreate
{
if
v
!=
nil
{
_c
.
SetDefaultMappedModel
(
*
v
)
}
return
_c
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_c
*
GroupCreate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
GroupCreate
{
_c
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
...
@@ -613,6 +641,14 @@ func (_c *GroupCreate) defaults() error {
v
:=
group
.
DefaultSortOrder
_c
.
mutation
.
SetSortOrder
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
AllowMessagesDispatch
();
!
ok
{
v
:=
group
.
DefaultAllowMessagesDispatch
_c
.
mutation
.
SetAllowMessagesDispatch
(
v
)
}
if
_
,
ok
:=
_c
.
mutation
.
DefaultMappedModel
();
!
ok
{
v
:=
group
.
DefaultDefaultMappedModel
_c
.
mutation
.
SetDefaultMappedModel
(
v
)
}
return
nil
}
...
...
@@ -683,6 +719,17 @@ func (_c *GroupCreate) check() error {
if
_
,
ok
:=
_c
.
mutation
.
SortOrder
();
!
ok
{
return
&
ValidationError
{
Name
:
"sort_order"
,
err
:
errors
.
New
(
`ent: missing required field "Group.sort_order"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
AllowMessagesDispatch
();
!
ok
{
return
&
ValidationError
{
Name
:
"allow_messages_dispatch"
,
err
:
errors
.
New
(
`ent: missing required field "Group.allow_messages_dispatch"`
)}
}
if
_
,
ok
:=
_c
.
mutation
.
DefaultMappedModel
();
!
ok
{
return
&
ValidationError
{
Name
:
"default_mapped_model"
,
err
:
errors
.
New
(
`ent: missing required field "Group.default_mapped_model"`
)}
}
if
v
,
ok
:=
_c
.
mutation
.
DefaultMappedModel
();
ok
{
if
err
:=
group
.
DefaultMappedModelValidator
(
v
);
err
!=
nil
{
return
&
ValidationError
{
Name
:
"default_mapped_model"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "Group.default_mapped_model": %w`
,
err
)}
}
}
return
nil
}
...
...
@@ -830,6 +877,14 @@ func (_c *GroupCreate) createSpec() (*Group, *sqlgraph.CreateSpec) {
_spec
.
SetField
(
group
.
FieldSortOrder
,
field
.
TypeInt
,
value
)
_node
.
SortOrder
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
AllowMessagesDispatch
();
ok
{
_spec
.
SetField
(
group
.
FieldAllowMessagesDispatch
,
field
.
TypeBool
,
value
)
_node
.
AllowMessagesDispatch
=
value
}
if
value
,
ok
:=
_c
.
mutation
.
DefaultMappedModel
();
ok
{
_spec
.
SetField
(
group
.
FieldDefaultMappedModel
,
field
.
TypeString
,
value
)
_node
.
DefaultMappedModel
=
value
}
if
nodes
:=
_c
.
mutation
.
APIKeysIDs
();
len
(
nodes
)
>
0
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
...
...
@@ -1520,6 +1575,30 @@ func (u *GroupUpsert) AddSortOrder(v int) *GroupUpsert {
return
u
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
u
*
GroupUpsert
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupUpsert
{
u
.
Set
(
group
.
FieldAllowMessagesDispatch
,
v
)
return
u
}
// UpdateAllowMessagesDispatch sets the "allow_messages_dispatch" field to the value that was provided on create.
func
(
u
*
GroupUpsert
)
UpdateAllowMessagesDispatch
()
*
GroupUpsert
{
u
.
SetExcluded
(
group
.
FieldAllowMessagesDispatch
)
return
u
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
u
*
GroupUpsert
)
SetDefaultMappedModel
(
v
string
)
*
GroupUpsert
{
u
.
Set
(
group
.
FieldDefaultMappedModel
,
v
)
return
u
}
// UpdateDefaultMappedModel sets the "default_mapped_model" field to the value that was provided on create.
func
(
u
*
GroupUpsert
)
UpdateDefaultMappedModel
()
*
GroupUpsert
{
u
.
SetExcluded
(
group
.
FieldDefaultMappedModel
)
return
u
}
// UpdateNewValues updates the mutable fields using the new values that were set on create.
// Using this option is equivalent to using:
//
...
...
@@ -2188,6 +2267,34 @@ func (u *GroupUpsertOne) UpdateSortOrder() *GroupUpsertOne {
})
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
u
*
GroupUpsertOne
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupUpsertOne
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
SetAllowMessagesDispatch
(
v
)
})
}
// UpdateAllowMessagesDispatch sets the "allow_messages_dispatch" field to the value that was provided on create.
func
(
u
*
GroupUpsertOne
)
UpdateAllowMessagesDispatch
()
*
GroupUpsertOne
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
UpdateAllowMessagesDispatch
()
})
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
u
*
GroupUpsertOne
)
SetDefaultMappedModel
(
v
string
)
*
GroupUpsertOne
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
SetDefaultMappedModel
(
v
)
})
}
// UpdateDefaultMappedModel sets the "default_mapped_model" field to the value that was provided on create.
func
(
u
*
GroupUpsertOne
)
UpdateDefaultMappedModel
()
*
GroupUpsertOne
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
UpdateDefaultMappedModel
()
})
}
// Exec executes the query.
func
(
u
*
GroupUpsertOne
)
Exec
(
ctx
context
.
Context
)
error
{
if
len
(
u
.
create
.
conflict
)
==
0
{
...
...
@@ -3022,6 +3129,34 @@ func (u *GroupUpsertBulk) UpdateSortOrder() *GroupUpsertBulk {
})
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
u
*
GroupUpsertBulk
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupUpsertBulk
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
SetAllowMessagesDispatch
(
v
)
})
}
// UpdateAllowMessagesDispatch sets the "allow_messages_dispatch" field to the value that was provided on create.
func
(
u
*
GroupUpsertBulk
)
UpdateAllowMessagesDispatch
()
*
GroupUpsertBulk
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
UpdateAllowMessagesDispatch
()
})
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
u
*
GroupUpsertBulk
)
SetDefaultMappedModel
(
v
string
)
*
GroupUpsertBulk
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
SetDefaultMappedModel
(
v
)
})
}
// UpdateDefaultMappedModel sets the "default_mapped_model" field to the value that was provided on create.
func
(
u
*
GroupUpsertBulk
)
UpdateDefaultMappedModel
()
*
GroupUpsertBulk
{
return
u
.
Update
(
func
(
s
*
GroupUpsert
)
{
s
.
UpdateDefaultMappedModel
()
})
}
// Exec executes the query.
func
(
u
*
GroupUpsertBulk
)
Exec
(
ctx
context
.
Context
)
error
{
if
u
.
create
.
err
!=
nil
{
...
...
backend/ent/group_update.go
View file @
92d35409
...
...
@@ -625,6 +625,34 @@ func (_u *GroupUpdate) AddSortOrder(v int) *GroupUpdate {
return
_u
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
_u
*
GroupUpdate
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupUpdate
{
_u
.
mutation
.
SetAllowMessagesDispatch
(
v
)
return
_u
}
// SetNillableAllowMessagesDispatch sets the "allow_messages_dispatch" field if the given value is not nil.
func
(
_u
*
GroupUpdate
)
SetNillableAllowMessagesDispatch
(
v
*
bool
)
*
GroupUpdate
{
if
v
!=
nil
{
_u
.
SetAllowMessagesDispatch
(
*
v
)
}
return
_u
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
_u
*
GroupUpdate
)
SetDefaultMappedModel
(
v
string
)
*
GroupUpdate
{
_u
.
mutation
.
SetDefaultMappedModel
(
v
)
return
_u
}
// SetNillableDefaultMappedModel sets the "default_mapped_model" field if the given value is not nil.
func
(
_u
*
GroupUpdate
)
SetNillableDefaultMappedModel
(
v
*
string
)
*
GroupUpdate
{
if
v
!=
nil
{
_u
.
SetDefaultMappedModel
(
*
v
)
}
return
_u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_u
*
GroupUpdate
)
AddAPIKeyIDs
(
ids
...
int64
)
*
GroupUpdate
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
...
@@ -910,6 +938,11 @@ func (_u *GroupUpdate) check() error {
return
&
ValidationError
{
Name
:
"subscription_type"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "Group.subscription_type": %w`
,
err
)}
}
}
if
v
,
ok
:=
_u
.
mutation
.
DefaultMappedModel
();
ok
{
if
err
:=
group
.
DefaultMappedModelValidator
(
v
);
err
!=
nil
{
return
&
ValidationError
{
Name
:
"default_mapped_model"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "Group.default_mapped_model": %w`
,
err
)}
}
}
return
nil
}
...
...
@@ -1110,6 +1143,12 @@ func (_u *GroupUpdate) sqlSave(ctx context.Context) (_node int, err error) {
if
value
,
ok
:=
_u
.
mutation
.
AddedSortOrder
();
ok
{
_spec
.
AddField
(
group
.
FieldSortOrder
,
field
.
TypeInt
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AllowMessagesDispatch
();
ok
{
_spec
.
SetField
(
group
.
FieldAllowMessagesDispatch
,
field
.
TypeBool
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
DefaultMappedModel
();
ok
{
_spec
.
SetField
(
group
.
FieldDefaultMappedModel
,
field
.
TypeString
,
value
)
}
if
_u
.
mutation
.
APIKeysCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
...
...
@@ -2014,6 +2053,34 @@ func (_u *GroupUpdateOne) AddSortOrder(v int) *GroupUpdateOne {
return
_u
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func
(
_u
*
GroupUpdateOne
)
SetAllowMessagesDispatch
(
v
bool
)
*
GroupUpdateOne
{
_u
.
mutation
.
SetAllowMessagesDispatch
(
v
)
return
_u
}
// SetNillableAllowMessagesDispatch sets the "allow_messages_dispatch" field if the given value is not nil.
func
(
_u
*
GroupUpdateOne
)
SetNillableAllowMessagesDispatch
(
v
*
bool
)
*
GroupUpdateOne
{
if
v
!=
nil
{
_u
.
SetAllowMessagesDispatch
(
*
v
)
}
return
_u
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func
(
_u
*
GroupUpdateOne
)
SetDefaultMappedModel
(
v
string
)
*
GroupUpdateOne
{
_u
.
mutation
.
SetDefaultMappedModel
(
v
)
return
_u
}
// SetNillableDefaultMappedModel sets the "default_mapped_model" field if the given value is not nil.
func
(
_u
*
GroupUpdateOne
)
SetNillableDefaultMappedModel
(
v
*
string
)
*
GroupUpdateOne
{
if
v
!=
nil
{
_u
.
SetDefaultMappedModel
(
*
v
)
}
return
_u
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by IDs.
func
(
_u
*
GroupUpdateOne
)
AddAPIKeyIDs
(
ids
...
int64
)
*
GroupUpdateOne
{
_u
.
mutation
.
AddAPIKeyIDs
(
ids
...
)
...
...
@@ -2312,6 +2379,11 @@ func (_u *GroupUpdateOne) check() error {
return
&
ValidationError
{
Name
:
"subscription_type"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "Group.subscription_type": %w`
,
err
)}
}
}
if
v
,
ok
:=
_u
.
mutation
.
DefaultMappedModel
();
ok
{
if
err
:=
group
.
DefaultMappedModelValidator
(
v
);
err
!=
nil
{
return
&
ValidationError
{
Name
:
"default_mapped_model"
,
err
:
fmt
.
Errorf
(
`ent: validator failed for field "Group.default_mapped_model": %w`
,
err
)}
}
}
return
nil
}
...
...
@@ -2529,6 +2601,12 @@ func (_u *GroupUpdateOne) sqlSave(ctx context.Context) (_node *Group, err error)
if
value
,
ok
:=
_u
.
mutation
.
AddedSortOrder
();
ok
{
_spec
.
AddField
(
group
.
FieldSortOrder
,
field
.
TypeInt
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
AllowMessagesDispatch
();
ok
{
_spec
.
SetField
(
group
.
FieldAllowMessagesDispatch
,
field
.
TypeBool
,
value
)
}
if
value
,
ok
:=
_u
.
mutation
.
DefaultMappedModel
();
ok
{
_spec
.
SetField
(
group
.
FieldDefaultMappedModel
,
field
.
TypeString
,
value
)
}
if
_u
.
mutation
.
APIKeysCleared
()
{
edge
:=
&
sqlgraph
.
EdgeSpec
{
Rel
:
sqlgraph
.
O2M
,
...
...
backend/ent/migrate/schema.go
View file @
92d35409
...
...
@@ -408,6 +408,8 @@ var (
{
Name
:
"mcp_xml_inject"
,
Type
:
field
.
TypeBool
,
Default
:
true
},
{
Name
:
"supported_model_scopes"
,
Type
:
field
.
TypeJSON
,
SchemaType
:
map
[
string
]
string
{
"postgres"
:
"jsonb"
}},
{
Name
:
"sort_order"
,
Type
:
field
.
TypeInt
,
Default
:
0
},
{
Name
:
"allow_messages_dispatch"
,
Type
:
field
.
TypeBool
,
Default
:
false
},
{
Name
:
"default_mapped_model"
,
Type
:
field
.
TypeString
,
Size
:
100
,
Default
:
""
},
}
// GroupsTable holds the schema information for the "groups" table.
GroupsTable
=
&
schema
.
Table
{
...
...
backend/ent/mutation.go
View file @
92d35409
...
...
@@ -8250,6 +8250,8 @@ type GroupMutation struct {
appendsupported_model_scopes []string
sort_order *int
addsort_order *int
allow_messages_dispatch *bool
default_mapped_model *string
clearedFields map[string]struct{}
api_keys map[int64]struct{}
removedapi_keys map[int64]struct{}
...
...
@@ -9994,6 +9996,78 @@ func (m *GroupMutation) ResetSortOrder() {
m.addsort_order = nil
}
// SetAllowMessagesDispatch sets the "allow_messages_dispatch" field.
func (m *GroupMutation) SetAllowMessagesDispatch(b bool) {
m.allow_messages_dispatch = &b
}
// AllowMessagesDispatch returns the value of the "allow_messages_dispatch" field in the mutation.
func (m *GroupMutation) AllowMessagesDispatch() (r bool, exists bool) {
v := m.allow_messages_dispatch
if v == nil {
return
}
return *v, true
}
// OldAllowMessagesDispatch returns the old "allow_messages_dispatch" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldAllowMessagesDispatch(ctx context.Context) (v bool, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldAllowMessagesDispatch is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldAllowMessagesDispatch requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldAllowMessagesDispatch: %w", err)
}
return oldValue.AllowMessagesDispatch, nil
}
// ResetAllowMessagesDispatch resets all changes to the "allow_messages_dispatch" field.
func (m *GroupMutation) ResetAllowMessagesDispatch() {
m.allow_messages_dispatch = nil
}
// SetDefaultMappedModel sets the "default_mapped_model" field.
func (m *GroupMutation) SetDefaultMappedModel(s string) {
m.default_mapped_model = &s
}
// DefaultMappedModel returns the value of the "default_mapped_model" field in the mutation.
func (m *GroupMutation) DefaultMappedModel() (r string, exists bool) {
v := m.default_mapped_model
if v == nil {
return
}
return *v, true
}
// OldDefaultMappedModel returns the old "default_mapped_model" field's value of the Group entity.
// If the Group 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 *GroupMutation) OldDefaultMappedModel(ctx context.Context) (v string, err error) {
if !m.op.Is(OpUpdateOne) {
return v, errors.New("OldDefaultMappedModel is only allowed on UpdateOne operations")
}
if m.id == nil || m.oldValue == nil {
return v, errors.New("OldDefaultMappedModel requires an ID field in the mutation")
}
oldValue, err := m.oldValue(ctx)
if err != nil {
return v, fmt.Errorf("querying old value for OldDefaultMappedModel: %w", err)
}
return oldValue.DefaultMappedModel, nil
}
// ResetDefaultMappedModel resets all changes to the "default_mapped_model" field.
func (m *GroupMutation) ResetDefaultMappedModel() {
m.default_mapped_model = nil
}
// AddAPIKeyIDs adds the "api_keys" edge to the APIKey entity by ids.
func (m *GroupMutation) AddAPIKeyIDs(ids ...int64) {
if m.api_keys == nil {
...
...
@@ -10352,7 +10426,7 @@ func (m *GroupMutation) Type() string {
// order to get all numeric fields that were incremented/decremented, call
// AddedFields().
func (m *GroupMutation) Fields() []string {
fields := make([]string, 0, 3
0
)
fields := make([]string, 0, 3
2
)
if m.created_at != nil {
fields = append(fields, group.FieldCreatedAt)
}
...
...
@@ -10443,6 +10517,12 @@ func (m *GroupMutation) Fields() []string {
if m.sort_order != nil {
fields = append(fields, group.FieldSortOrder)
}
if m.allow_messages_dispatch != nil {
fields = append(fields, group.FieldAllowMessagesDispatch)
}
if m.default_mapped_model != nil {
fields = append(fields, group.FieldDefaultMappedModel)
}
return fields
}
...
...
@@ -10511,6 +10591,10 @@ func (m *GroupMutation) Field(name string) (ent.Value, bool) {
return m.SupportedModelScopes()
case group.FieldSortOrder:
return m.SortOrder()
case group.FieldAllowMessagesDispatch:
return m.AllowMessagesDispatch()
case group.FieldDefaultMappedModel:
return m.DefaultMappedModel()
}
return nil, false
}
...
...
@@ -10580,6 +10664,10 @@ func (m *GroupMutation) OldField(ctx context.Context, name string) (ent.Value, e
return m.OldSupportedModelScopes(ctx)
case group.FieldSortOrder:
return m.OldSortOrder(ctx)
case group.FieldAllowMessagesDispatch:
return m.OldAllowMessagesDispatch(ctx)
case group.FieldDefaultMappedModel:
return m.OldDefaultMappedModel(ctx)
}
return nil, fmt.Errorf("unknown Group field %s", name)
}
...
...
@@ -10799,6 +10887,20 @@ func (m *GroupMutation) SetField(name string, value ent.Value) error {
}
m.SetSortOrder(v)
return nil
case group.FieldAllowMessagesDispatch:
v, ok := value.(bool)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetAllowMessagesDispatch(v)
return nil
case group.FieldDefaultMappedModel:
v, ok := value.(string)
if !ok {
return fmt.Errorf("unexpected type %T for field %s", value, name)
}
m.SetDefaultMappedModel(v)
return nil
}
return fmt.Errorf("unknown Group field %s", name)
}
...
...
@@ -11226,6 +11328,12 @@ func (m *GroupMutation) ResetField(name string) error {
case group.FieldSortOrder:
m.ResetSortOrder()
return nil
case group.FieldAllowMessagesDispatch:
m.ResetAllowMessagesDispatch()
return nil
case group.FieldDefaultMappedModel:
m.ResetDefaultMappedModel()
return nil
}
return fmt.Errorf("unknown Group field %s", name)
}
...
...
backend/ent/runtime/runtime.go
View file @
92d35409
...
...
@@ -453,6 +453,16 @@ func init() {
groupDescSortOrder
:=
groupFields
[
26
]
.
Descriptor
()
// group.DefaultSortOrder holds the default value on creation for the sort_order field.
group
.
DefaultSortOrder
=
groupDescSortOrder
.
Default
.
(
int
)
// groupDescAllowMessagesDispatch is the schema descriptor for allow_messages_dispatch field.
groupDescAllowMessagesDispatch
:=
groupFields
[
27
]
.
Descriptor
()
// group.DefaultAllowMessagesDispatch holds the default value on creation for the allow_messages_dispatch field.
group
.
DefaultAllowMessagesDispatch
=
groupDescAllowMessagesDispatch
.
Default
.
(
bool
)
// groupDescDefaultMappedModel is the schema descriptor for default_mapped_model field.
groupDescDefaultMappedModel
:=
groupFields
[
28
]
.
Descriptor
()
// group.DefaultDefaultMappedModel holds the default value on creation for the default_mapped_model field.
group
.
DefaultDefaultMappedModel
=
groupDescDefaultMappedModel
.
Default
.
(
string
)
// group.DefaultMappedModelValidator is a validator for the "default_mapped_model" field. It is called by the builders before save.
group
.
DefaultMappedModelValidator
=
groupDescDefaultMappedModel
.
Validators
[
0
]
.
(
func
(
string
)
error
)
idempotencyrecordMixin
:=
schema
.
IdempotencyRecord
{}
.
Mixin
()
idempotencyrecordMixinFields0
:=
idempotencyrecordMixin
[
0
]
.
Fields
()
_
=
idempotencyrecordMixinFields0
...
...
backend/ent/schema/group.go
View file @
92d35409
...
...
@@ -148,6 +148,15 @@ func (Group) Fields() []ent.Field {
field
.
Int
(
"sort_order"
)
.
Default
(
0
)
.
Comment
(
"分组显示排序,数值越小越靠前"
),
// OpenAI Messages 调度配置 (added by migration 069)
field
.
Bool
(
"allow_messages_dispatch"
)
.
Default
(
false
)
.
Comment
(
"是否允许 /v1/messages 调度到此 OpenAI 分组"
),
field
.
String
(
"default_mapped_model"
)
.
MaxLen
(
100
)
.
Default
(
""
)
.
Comment
(
"默认映射模型 ID,当账号级映射找不到时使用此值"
),
}
}
...
...
backend/internal/handler/admin/group_handler.go
View file @
92d35409
...
...
@@ -53,6 +53,9 @@ type CreateGroupRequest struct {
SupportedModelScopes
[]
string
`json:"supported_model_scopes"`
// Sora 存储配额
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
DefaultMappedModel
string
`json:"default_mapped_model"`
// 从指定分组复制账号(创建后自动绑定)
CopyAccountsFromGroupIDs
[]
int64
`json:"copy_accounts_from_group_ids"`
}
...
...
@@ -88,6 +91,9 @@ type UpdateGroupRequest struct {
SupportedModelScopes
*
[]
string
`json:"supported_model_scopes"`
// Sora 存储配额
SoraStorageQuotaBytes
*
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
*
bool
`json:"allow_messages_dispatch"`
DefaultMappedModel
*
string
`json:"default_mapped_model"`
// 从指定分组复制账号(同步操作:先清空当前分组的账号绑定,再绑定源分组的账号)
CopyAccountsFromGroupIDs
[]
int64
`json:"copy_accounts_from_group_ids"`
}
...
...
@@ -203,6 +209,8 @@ func (h *GroupHandler) Create(c *gin.Context) {
MCPXMLInject
:
req
.
MCPXMLInject
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
DefaultMappedModel
:
req
.
DefaultMappedModel
,
CopyAccountsFromGroupIDs
:
req
.
CopyAccountsFromGroupIDs
,
})
if
err
!=
nil
{
...
...
@@ -254,6 +262,8 @@ func (h *GroupHandler) Update(c *gin.Context) {
MCPXMLInject
:
req
.
MCPXMLInject
,
SupportedModelScopes
:
req
.
SupportedModelScopes
,
SoraStorageQuotaBytes
:
req
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
req
.
AllowMessagesDispatch
,
DefaultMappedModel
:
req
.
DefaultMappedModel
,
CopyAccountsFromGroupIDs
:
req
.
CopyAccountsFromGroupIDs
,
})
if
err
!=
nil
{
...
...
backend/internal/handler/dto/mappers.go
View file @
92d35409
...
...
@@ -125,8 +125,9 @@ func GroupFromServiceAdmin(g *service.Group) *AdminGroup {
Group
:
groupFromServiceBase
(
g
),
ModelRouting
:
g
.
ModelRouting
,
ModelRoutingEnabled
:
g
.
ModelRoutingEnabled
,
MCPXMLInject
:
g
.
MCPXMLInject
,
SupportedModelScopes
:
g
.
SupportedModelScopes
,
MCPXMLInject
:
g
.
MCPXMLInject
,
DefaultMappedModel
:
g
.
DefaultMappedModel
,
SupportedModelScopes
:
g
.
SupportedModelScopes
,
AccountCount
:
g
.
AccountCount
,
SortOrder
:
g
.
SortOrder
,
}
...
...
@@ -164,6 +165,7 @@ func groupFromServiceBase(g *service.Group) Group {
FallbackGroupID
:
g
.
FallbackGroupID
,
FallbackGroupIDOnInvalidRequest
:
g
.
FallbackGroupIDOnInvalidRequest
,
SoraStorageQuotaBytes
:
g
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
g
.
AllowMessagesDispatch
,
CreatedAt
:
g
.
CreatedAt
,
UpdatedAt
:
g
.
UpdatedAt
,
}
...
...
backend/internal/handler/dto/types.go
View file @
92d35409
...
...
@@ -96,6 +96,9 @@ type Group struct {
// Sora 存储配额
SoraStorageQuotaBytes
int64
`json:"sora_storage_quota_bytes"`
// OpenAI Messages 调度开关(用户侧需要此字段判断是否展示 Claude Code 教程)
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
CreatedAt
time
.
Time
`json:"created_at"`
UpdatedAt
time
.
Time
`json:"updated_at"`
}
...
...
@@ -112,6 +115,9 @@ type AdminGroup struct {
// MCP XML 协议注入(仅 antigravity 平台使用)
MCPXMLInject
bool
`json:"mcp_xml_inject"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
DefaultMappedModel
string
`json:"default_mapped_model"`
// 支持的模型系列(仅 antigravity 平台使用)
SupportedModelScopes
[]
string
`json:"supported_model_scopes"`
AccountGroups
[]
AccountGroup
`json:"account_groups,omitempty"`
...
...
backend/internal/handler/openai_gateway_handler.go
View file @
92d35409
...
...
@@ -467,6 +467,14 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
zap
.
Int64
(
"api_key_id"
,
apiKey
.
ID
),
zap
.
Any
(
"group_id"
,
apiKey
.
GroupID
),
)
// 检查分组是否允许 /v1/messages 调度
if
apiKey
.
Group
!=
nil
&&
!
apiKey
.
Group
.
AllowMessagesDispatch
{
h
.
anthropicErrorResponse
(
c
,
http
.
StatusForbidden
,
"permission_error"
,
"This group does not allow /v1/messages dispatch"
)
return
}
if
!
h
.
ensureResponsesDependencies
(
c
,
reqLog
)
{
return
}
...
...
@@ -536,6 +544,8 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
var
lastFailoverErr
*
service
.
UpstreamFailoverError
for
{
// 清除上一次迭代的降级模型标记,避免残留影响本次迭代
c
.
Set
(
"openai_messages_fallback_model"
,
""
)
reqLog
.
Debug
(
"openai_messages.account_selecting"
,
zap
.
Int
(
"excluded_account_count"
,
len
(
failedAccountIDs
)))
selection
,
scheduleDecision
,
err
:=
h
.
gatewayService
.
SelectAccountWithScheduler
(
c
.
Request
.
Context
(),
...
...
@@ -551,16 +561,41 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
zap
.
Error
(
err
),
zap
.
Int
(
"excluded_account_count"
,
len
(
failedAccountIDs
)),
)
// 首次调度失败 + 有默认映射模型 → 用默认模型重试
if
len
(
failedAccountIDs
)
==
0
{
h
.
anthropicStreamingAwareError
(
c
,
http
.
StatusServiceUnavailable
,
"api_error"
,
"Service temporarily unavailable"
,
streamStarted
)
return
}
if
lastFailoverErr
!=
nil
{
h
.
handleAnthropicFailoverExhausted
(
c
,
lastFailoverErr
,
streamStarted
)
defaultModel
:=
""
if
apiKey
.
Group
!=
nil
{
defaultModel
=
apiKey
.
Group
.
DefaultMappedModel
}
if
defaultModel
!=
""
&&
defaultModel
!=
reqModel
{
reqLog
.
Info
(
"openai_messages.fallback_to_default_model"
,
zap
.
String
(
"default_mapped_model"
,
defaultModel
),
)
selection
,
scheduleDecision
,
err
=
h
.
gatewayService
.
SelectAccountWithScheduler
(
c
.
Request
.
Context
(),
apiKey
.
GroupID
,
""
,
sessionHash
,
defaultModel
,
failedAccountIDs
,
service
.
OpenAIUpstreamTransportAny
,
)
if
err
==
nil
&&
selection
!=
nil
{
c
.
Set
(
"openai_messages_fallback_model"
,
defaultModel
)
}
}
if
err
!=
nil
{
h
.
anthropicStreamingAwareError
(
c
,
http
.
StatusServiceUnavailable
,
"api_error"
,
"Service temporarily unavailable"
,
streamStarted
)
return
}
}
else
{
h
.
anthropicStreamingAwareError
(
c
,
http
.
StatusBadGateway
,
"api_error"
,
"Upstream request failed"
,
streamStarted
)
if
lastFailoverErr
!=
nil
{
h
.
handleAnthropicFailoverExhausted
(
c
,
lastFailoverErr
,
streamStarted
)
}
else
{
h
.
anthropicStreamingAwareError
(
c
,
http
.
StatusBadGateway
,
"api_error"
,
"Upstream request failed"
,
streamStarted
)
}
return
}
return
}
if
selection
==
nil
||
selection
.
Account
==
nil
{
h
.
anthropicStreamingAwareError
(
c
,
http
.
StatusServiceUnavailable
,
"api_error"
,
"No available accounts"
,
streamStarted
)
...
...
@@ -579,7 +614,15 @@ func (h *OpenAIGatewayHandler) Messages(c *gin.Context) {
service
.
SetOpsLatencyMs
(
c
,
service
.
OpsRoutingLatencyMsKey
,
time
.
Since
(
routingStart
)
.
Milliseconds
())
forwardStart
:=
time
.
Now
()
result
,
err
:=
h
.
gatewayService
.
ForwardAsAnthropic
(
c
.
Request
.
Context
(),
c
,
account
,
body
,
promptCacheKey
)
defaultMappedModel
:=
""
if
apiKey
.
Group
!=
nil
{
defaultMappedModel
=
apiKey
.
Group
.
DefaultMappedModel
}
// 如果使用了降级模型调度,强制使用降级模型
if
fallbackModel
:=
c
.
GetString
(
"openai_messages_fallback_model"
);
fallbackModel
!=
""
{
defaultMappedModel
=
fallbackModel
}
result
,
err
:=
h
.
gatewayService
.
ForwardAsAnthropic
(
c
.
Request
.
Context
(),
c
,
account
,
body
,
promptCacheKey
,
defaultMappedModel
)
forwardDurationMs
:=
time
.
Since
(
forwardStart
)
.
Milliseconds
()
if
accountReleaseFunc
!=
nil
{
...
...
backend/internal/repository/api_key_repo.go
View file @
92d35409
...
...
@@ -165,6 +165,8 @@ func (r *apiKeyRepository) GetByKeyForAuth(ctx context.Context, key string) (*se
group
.
FieldModelRouting
,
group
.
FieldMcpXMLInject
,
group
.
FieldSupportedModelScopes
,
group
.
FieldAllowMessagesDispatch
,
group
.
FieldDefaultMappedModel
,
)
})
.
Only
(
ctx
)
...
...
@@ -619,6 +621,8 @@ func groupEntityToService(g *dbent.Group) *service.Group {
MCPXMLInject
:
g
.
McpXMLInject
,
SupportedModelScopes
:
g
.
SupportedModelScopes
,
SortOrder
:
g
.
SortOrder
,
AllowMessagesDispatch
:
g
.
AllowMessagesDispatch
,
DefaultMappedModel
:
g
.
DefaultMappedModel
,
CreatedAt
:
g
.
CreatedAt
,
UpdatedAt
:
g
.
UpdatedAt
,
}
...
...
backend/internal/repository/group_repo.go
View file @
92d35409
...
...
@@ -59,7 +59,9 @@ func (r *groupRepository) Create(ctx context.Context, groupIn *service.Group) er
SetNillableFallbackGroupIDOnInvalidRequest
(
groupIn
.
FallbackGroupIDOnInvalidRequest
)
.
SetModelRoutingEnabled
(
groupIn
.
ModelRoutingEnabled
)
.
SetMcpXMLInject
(
groupIn
.
MCPXMLInject
)
.
SetSoraStorageQuotaBytes
(
groupIn
.
SoraStorageQuotaBytes
)
SetSoraStorageQuotaBytes
(
groupIn
.
SoraStorageQuotaBytes
)
.
SetAllowMessagesDispatch
(
groupIn
.
AllowMessagesDispatch
)
.
SetDefaultMappedModel
(
groupIn
.
DefaultMappedModel
)
// 设置模型路由配置
if
groupIn
.
ModelRouting
!=
nil
{
...
...
@@ -125,7 +127,9 @@ func (r *groupRepository) Update(ctx context.Context, groupIn *service.Group) er
SetClaudeCodeOnly
(
groupIn
.
ClaudeCodeOnly
)
.
SetModelRoutingEnabled
(
groupIn
.
ModelRoutingEnabled
)
.
SetMcpXMLInject
(
groupIn
.
MCPXMLInject
)
.
SetSoraStorageQuotaBytes
(
groupIn
.
SoraStorageQuotaBytes
)
SetSoraStorageQuotaBytes
(
groupIn
.
SoraStorageQuotaBytes
)
.
SetAllowMessagesDispatch
(
groupIn
.
AllowMessagesDispatch
)
.
SetDefaultMappedModel
(
groupIn
.
DefaultMappedModel
)
// 显式处理可空字段:nil 需要 clear,非 nil 需要 set。
if
groupIn
.
DailyLimitUSD
!=
nil
{
...
...
backend/internal/service/admin_service.go
View file @
92d35409
...
...
@@ -145,6 +145,9 @@ type CreateGroupInput struct {
SupportedModelScopes
[]
string
// Sora 存储配额
SoraStorageQuotaBytes
int64
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
bool
DefaultMappedModel
string
// 从指定分组复制账号(创建分组后在同一事务内绑定)
CopyAccountsFromGroupIDs
[]
int64
}
...
...
@@ -181,6 +184,9 @@ type UpdateGroupInput struct {
SupportedModelScopes
*
[]
string
// Sora 存储配额
SoraStorageQuotaBytes
*
int64
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
*
bool
DefaultMappedModel
*
string
// 从指定分组复制账号(同步操作:先清空当前分组的账号绑定,再绑定源分组的账号)
CopyAccountsFromGroupIDs
[]
int64
}
...
...
@@ -909,6 +915,8 @@ func (s *adminServiceImpl) CreateGroup(ctx context.Context, input *CreateGroupIn
MCPXMLInject
:
mcpXMLInject
,
SupportedModelScopes
:
input
.
SupportedModelScopes
,
SoraStorageQuotaBytes
:
input
.
SoraStorageQuotaBytes
,
AllowMessagesDispatch
:
input
.
AllowMessagesDispatch
,
DefaultMappedModel
:
input
.
DefaultMappedModel
,
}
if
err
:=
s
.
groupRepo
.
Create
(
ctx
,
group
);
err
!=
nil
{
return
nil
,
err
...
...
@@ -1122,6 +1130,14 @@ func (s *adminServiceImpl) UpdateGroup(ctx context.Context, id int64, input *Upd
group
.
SupportedModelScopes
=
*
input
.
SupportedModelScopes
}
// OpenAI Messages 调度配置
if
input
.
AllowMessagesDispatch
!=
nil
{
group
.
AllowMessagesDispatch
=
*
input
.
AllowMessagesDispatch
}
if
input
.
DefaultMappedModel
!=
nil
{
group
.
DefaultMappedModel
=
*
input
.
DefaultMappedModel
}
if
err
:=
s
.
groupRepo
.
Update
(
ctx
,
group
);
err
!=
nil
{
return
nil
,
err
}
...
...
backend/internal/service/api_key_auth_cache.go
View file @
92d35409
...
...
@@ -65,6 +65,10 @@ type APIKeyAuthGroupSnapshot struct {
// 支持的模型系列(仅 antigravity 平台使用)
SupportedModelScopes
[]
string
`json:"supported_model_scopes,omitempty"`
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
bool
`json:"allow_messages_dispatch"`
DefaultMappedModel
string
`json:"default_mapped_model,omitempty"`
}
// APIKeyAuthCacheEntry 缓存条目,支持负缓存
...
...
backend/internal/service/api_key_auth_cache_impl.go
View file @
92d35409
...
...
@@ -245,6 +245,8 @@ func (s *APIKeyService) snapshotFromAPIKey(apiKey *APIKey) *APIKeyAuthSnapshot {
ModelRoutingEnabled
:
apiKey
.
Group
.
ModelRoutingEnabled
,
MCPXMLInject
:
apiKey
.
Group
.
MCPXMLInject
,
SupportedModelScopes
:
apiKey
.
Group
.
SupportedModelScopes
,
AllowMessagesDispatch
:
apiKey
.
Group
.
AllowMessagesDispatch
,
DefaultMappedModel
:
apiKey
.
Group
.
DefaultMappedModel
,
}
}
return
snapshot
...
...
@@ -302,6 +304,8 @@ func (s *APIKeyService) snapshotToAPIKey(key string, snapshot *APIKeyAuthSnapsho
ModelRoutingEnabled
:
snapshot
.
Group
.
ModelRoutingEnabled
,
MCPXMLInject
:
snapshot
.
Group
.
MCPXMLInject
,
SupportedModelScopes
:
snapshot
.
Group
.
SupportedModelScopes
,
AllowMessagesDispatch
:
snapshot
.
Group
.
AllowMessagesDispatch
,
DefaultMappedModel
:
snapshot
.
Group
.
DefaultMappedModel
,
}
}
s
.
compileAPIKeyIPRules
(
apiKey
)
...
...
backend/internal/service/group.go
View file @
92d35409
...
...
@@ -57,6 +57,10 @@ type Group struct {
// 分组排序
SortOrder
int
// OpenAI Messages 调度配置(仅 openai 平台使用)
AllowMessagesDispatch
bool
DefaultMappedModel
string
CreatedAt
time
.
Time
UpdatedAt
time
.
Time
...
...
backend/internal/service/openai_gateway_messages.go
View file @
92d35409
...
...
@@ -28,6 +28,7 @@ func (s *OpenAIGatewayService) ForwardAsAnthropic(
account
*
Account
,
body
[]
byte
,
promptCacheKey
string
,
defaultMappedModel
string
,
)
(
*
OpenAIForwardResult
,
error
)
{
startTime
:=
time
.
Now
()
...
...
@@ -47,6 +48,10 @@ func (s *OpenAIGatewayService) ForwardAsAnthropic(
// 3. Model mapping
mappedModel
:=
account
.
GetMappedModel
(
originalModel
)
// 分组级降级:账号未映射时使用分组默认映射模型
if
mappedModel
==
originalModel
&&
defaultMappedModel
!=
""
{
mappedModel
=
defaultMappedModel
}
responsesReq
.
Model
=
mappedModel
logger
.
L
()
.
Debug
(
"openai messages: model mapping applied"
,
...
...
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