Files
sub2api-cn-relay-manager/internal/access/gateway_validation.go
2026-05-23 17:06:52 +08:00

83 lines
2.9 KiB
Go

package access
import (
"context"
"fmt"
"strings"
"time"
"sub2api-cn-relay-manager/internal/host/sub2api"
)
func (s *Service) verifyGatewayClosure(ctx context.Context, req ClosureRequest, plan closurePlan) (sub2api.GatewayAccessResult, error) {
if plan.effectiveProbeAPIKey == "" {
return sub2api.GatewayAccessResult{}, fmt.Errorf("access probe api key is required to verify gateway closure")
}
result, err := s.host.CheckGatewayAccess(ctx, sub2api.GatewayAccessCheckRequest{
APIKey: plan.effectiveProbeAPIKey,
ExpectedModel: req.ExpectedModel,
})
if err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("check gateway access: %w", err)
}
result.EffectiveProbeAPIKey = plan.effectiveProbeAPIKey
result.EffectiveProbeKeySource = plan.effectiveProbeKeySource
if result.OK && result.HasExpectedModel && strings.TrimSpace(req.ExpectedModel) != "" {
completionReq := sub2api.GatewayCompletionCheckRequest{
APIKey: plan.effectiveProbeAPIKey,
Model: req.ExpectedModel,
Prompt: req.Prompt,
MaxTokens: req.MaxTokens,
}
completion, err := s.checkGatewayCompletionWithRetry(ctx, completionReq)
if err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("check gateway completion: %w", err)
}
completion, err = s.maybeRepairOpenAIResponsesCapability(ctx, req, completionReq, completion)
if err != nil {
return sub2api.GatewayAccessResult{}, fmt.Errorf("re-check gateway completion after capability repair: %w", err)
}
result.CompletionOK = completion.OK
result.CompletionStatus = completion.StatusCode
result.CompletionType = completion.ContentType
result.CompletionBody = completion.BodyPreview
}
return result, nil
}
func (s *Service) checkGatewayCompletionWithRetry(ctx context.Context, req sub2api.GatewayCompletionCheckRequest) (sub2api.GatewayCompletionResult, error) {
var last sub2api.GatewayCompletionResult
for attempt := 1; attempt <= gatewayCompletionRetryAttempts; attempt++ {
completion, err := s.host.CheckGatewayCompletion(ctx, req)
if err != nil {
return sub2api.GatewayCompletionResult{}, err
}
last = completion
if completion.OK || !isTransientGatewayCompletionFailure(completion) || attempt == gatewayCompletionRetryAttempts {
return completion, nil
}
timer := time.NewTimer(gatewayCompletionRetryDelay)
select {
case <-ctx.Done():
timer.Stop()
return last, ctx.Err()
case <-timer.C:
}
}
return last, nil
}
func isTransientGatewayCompletionFailure(result sub2api.GatewayCompletionResult) bool {
if result.OK {
return false
}
if result.StatusCode != 0 && result.StatusCode != 429 && result.StatusCode != 502 && result.StatusCode != 503 && result.StatusCode != 504 {
return false
}
body := strings.ToLower(strings.TrimSpace(result.BodyPreview))
return strings.Contains(body, "service temporarily unavailable") ||
strings.Contains(body, "no available accounts") ||
strings.Contains(body, "temporar") ||
strings.Contains(body, "try again")
}