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") }