x
Some checks are pending
CI / golangci-lint (push) Waiting to run
CI / windsurf-platform (macos-latest) (push) Waiting to run
CI / windsurf-platform (windows-latest) (push) Waiting to run
CI / test (push) Waiting to run
CI / frontend (push) Waiting to run
Security Scan / backend-security (push) Waiting to run
Security Scan / frontend-security (push) Waiting to run
Some checks are pending
CI / golangci-lint (push) Waiting to run
CI / windsurf-platform (macos-latest) (push) Waiting to run
CI / windsurf-platform (windows-latest) (push) Waiting to run
CI / test (push) Waiting to run
CI / frontend (push) Waiting to run
Security Scan / backend-security (push) Waiting to run
Security Scan / frontend-security (push) Waiting to run
This commit is contained in:
parent
a420179abb
commit
3cffaa1e8e
@ -22,16 +22,18 @@ const (
|
||||
PlatformOpenAI = "openai"
|
||||
PlatformGemini = "gemini"
|
||||
PlatformAntigravity = "antigravity"
|
||||
PlatformWindsurf = "windsurf"
|
||||
)
|
||||
|
||||
// Account type constants
|
||||
const (
|
||||
AccountTypeOAuth = "oauth" // OAuth类型账号(full scope: profile + inference)
|
||||
AccountTypeSetupToken = "setup-token" // Setup Token类型账号(inference only scope)
|
||||
AccountTypeAPIKey = "apikey" // API Key类型账号
|
||||
AccountTypeUpstream = "upstream" // 上游透传类型账号(通过 Base URL + API Key 连接上游)
|
||||
AccountTypeBedrock = "bedrock" // AWS Bedrock 类型账号(通过 SigV4 签名或 API Key 连接 Bedrock,由 credentials.auth_mode 区分)
|
||||
AccountTypeServiceAccount = "service_account" // Google Service Account 类型账号(用于 Vertex AI)
|
||||
AccountTypeOAuth = "oauth" // OAuth类型账号(full scope: profile + inference)
|
||||
AccountTypeSetupToken = "setup-token" // Setup Token类型账号(inference only scope)
|
||||
AccountTypeAPIKey = "apikey" // API Key类型账号
|
||||
AccountTypeUpstream = "upstream" // 上游透传类型账号(通过 Base URL + API Key 连接上游)
|
||||
AccountTypeBedrock = "bedrock" // AWS Bedrock 类型账号(通过 SigV4 签名或 API Key 连接 Bedrock,由 credentials.auth_mode 区分)
|
||||
AccountTypeServiceAccount = "service_account" // Google Service Account 类型账号(用于 Vertex AI)
|
||||
AccountTypeWindsurfSession = "windsurf-session" // Windsurf Session 类型账号(邮箱密码登录获取的 session token + api_key)
|
||||
)
|
||||
|
||||
// Redeem type constants
|
||||
|
||||
@ -417,6 +417,16 @@ type GatewayCache interface {
|
||||
// DeleteSessionAccountID 删除粘性会话绑定,用于账号不可用时主动清理
|
||||
// Delete sticky session binding, used to proactively clean up when account becomes unavailable
|
||||
DeleteSessionAccountID(ctx context.Context, groupID int64, sessionHash string) error
|
||||
|
||||
// GetCascadeID 获取 Windsurf Cascade 会话 ID(用于 LS 多轮复用)
|
||||
// Get the Windsurf Cascade ID bound to a chat session for multi-turn LS reuse.
|
||||
GetCascadeID(ctx context.Context, key string) (string, error)
|
||||
// SetCascadeID 写入 Cascade 会话 ID
|
||||
// Persist the Cascade session ID with the given TTL.
|
||||
SetCascadeID(ctx context.Context, key string, cascadeID string, ttl time.Duration) error
|
||||
// DeleteCascadeID 失效 Cascade 会话 ID(panel-not-found / 错误时调用)
|
||||
// Invalidate the cached Cascade session ID on panel-not-found or upstream error.
|
||||
DeleteCascadeID(ctx context.Context, key string) error
|
||||
}
|
||||
|
||||
// derefGroupID safely dereferences *int64 to int64, returning 0 if nil
|
||||
@ -580,6 +590,7 @@ type GatewayService struct {
|
||||
claudeTokenProvider *ClaudeTokenProvider
|
||||
sessionLimitCache SessionLimitCache // 会话数量限制缓存(仅 Anthropic OAuth/SetupToken)
|
||||
rpmCache RPMCache // RPM 计数缓存(仅 Anthropic OAuth/SetupToken)
|
||||
rpmTokenBucket *RPMTokenBucketService // RPM 令牌桶平滑(可选,由配置开关控制)
|
||||
userGroupRateResolver *userGroupRateResolver
|
||||
userGroupRateCache *gocache.Cache
|
||||
userGroupRateSF singleflight.Group
|
||||
@ -625,6 +636,7 @@ func NewGatewayService(
|
||||
channelService *ChannelService,
|
||||
resolver *ModelPricingResolver,
|
||||
balanceNotifyService *BalanceNotifyService,
|
||||
rpmTokenBucketSvc *RPMTokenBucketService,
|
||||
userPlatformQuotaRepo UserPlatformQuotaRepository,
|
||||
) *GatewayService {
|
||||
userGroupRateTTL := resolveUserGroupRateCacheTTL(cfg)
|
||||
@ -652,6 +664,7 @@ func NewGatewayService(
|
||||
claudeTokenProvider: claudeTokenProvider,
|
||||
sessionLimitCache: sessionLimitCache,
|
||||
rpmCache: rpmCache,
|
||||
rpmTokenBucket: rpmTokenBucketSvc,
|
||||
userGroupRateCache: gocache.New(userGroupRateTTL, time.Minute),
|
||||
settingService: settingService,
|
||||
modelsListCache: gocache.New(modelsListTTL, time.Minute),
|
||||
@ -2361,6 +2374,23 @@ func (s *GatewayService) IsSingleAntigravityAccountGroup(ctx context.Context, gr
|
||||
return len(accounts) == 1
|
||||
}
|
||||
|
||||
func (s *GatewayService) IsSingleWindsurfAccountGroup(ctx context.Context, groupID *int64) bool {
|
||||
accounts, _, err := s.listSchedulableAccounts(ctx, groupID, PlatformWindsurf, true)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return len(accounts) == 1
|
||||
}
|
||||
|
||||
// AcquireRPMToken consumes one RPM token for the given account, waiting up to maxWait if needed.
|
||||
// Returns nil immediately when RPM smoothing is not configured or the account has no RPM limit.
|
||||
func (s *GatewayService) AcquireRPMToken(ctx context.Context, accountID int64, rpm int, maxWait time.Duration) error {
|
||||
if s.rpmTokenBucket == nil {
|
||||
return nil
|
||||
}
|
||||
return s.rpmTokenBucket.AcquireWithWait(ctx, accountID, rpm, maxWait)
|
||||
}
|
||||
|
||||
func (s *GatewayService) isAccountAllowedForPlatform(account *Account, platform string, useMixed bool) bool {
|
||||
if account == nil {
|
||||
return false
|
||||
|
||||
@ -1393,11 +1393,11 @@ func (s *defaultOpenAIAccountScheduler) selectByPowerOfTwo(
|
||||
}
|
||||
|
||||
tryAcquire := func(c openAIAccountCandidateScore) (*AccountSelectionResult, bool, error) {
|
||||
fresh := s.service.resolveFreshSchedulableOpenAIAccount(ctx, c.account, req.RequestedModel, req.RequireCompact)
|
||||
fresh := s.service.resolveFreshSchedulableOpenAIAccount(ctx, c.account, req.RequestedModel, req.RequireCompact, req.RequiredCapability)
|
||||
if fresh == nil || !s.isAccountTransportCompatible(fresh, req.RequiredTransport) {
|
||||
return nil, false, nil
|
||||
}
|
||||
fresh = s.service.recheckSelectedOpenAIAccountFromDB(ctx, fresh, req.RequestedModel, req.RequireCompact)
|
||||
fresh = s.service.recheckSelectedOpenAIAccountFromDB(ctx, fresh, req.RequestedModel, req.RequireCompact, req.RequiredCapability)
|
||||
if fresh == nil || !s.isAccountTransportCompatible(fresh, req.RequiredTransport) {
|
||||
return nil, false, nil
|
||||
}
|
||||
@ -1431,7 +1431,7 @@ func (s *defaultOpenAIAccountScheduler) selectByPowerOfTwo(
|
||||
// Both slots busy — return wait plan on the better candidate.
|
||||
cfg := s.service.schedulingConfig()
|
||||
for _, c := range []openAIAccountCandidateScore{first, second} {
|
||||
fresh := s.service.resolveFreshSchedulableOpenAIAccount(ctx, c.account, req.RequestedModel, req.RequireCompact)
|
||||
fresh := s.service.resolveFreshSchedulableOpenAIAccount(ctx, c.account, req.RequestedModel, req.RequireCompact, req.RequiredCapability)
|
||||
if fresh == nil || !s.isAccountTransportCompatible(fresh, req.RequiredTransport) {
|
||||
continue
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user