openai_gateway_model_availability.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. package service
  2. import (
  3. "context"
  4. "strings"
  5. "github.com/Wei-Shaw/sub2api/internal/config"
  6. )
  7. // DiagnoseModelAvailabilityForPlatform reports whether the requested model
  8. // is configured to be served by any persistently eligible OpenAI-compatible
  9. // account in the group for the given platform (e.g. PlatformOpenAI,
  10. // PlatformGrok). The platform scopes the candidate pool so distinct
  11. // OpenAI-compatible platforms do not cross-contaminate diagnosis results.
  12. // The query bypasses scheduler snapshots and ignores transient runtime state.
  13. //
  14. // Safe to call on the error path: returns {true,true} on any internal
  15. // failure or when the inputs preclude meaningful diagnosis (empty model,
  16. // nil service), so callers stay on the 503 fallback branch.
  17. func (s *OpenAIGatewayService) DiagnoseModelAvailabilityForPlatform(
  18. ctx context.Context,
  19. groupID *int64,
  20. requestedModel string,
  21. platform string,
  22. ) ModelAvailabilityDiagnosis {
  23. if s == nil {
  24. return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
  25. }
  26. requestedModel = strings.TrimSpace(requestedModel)
  27. if requestedModel == "" {
  28. return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
  29. }
  30. if s.accountRepo == nil {
  31. return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
  32. }
  33. platform = NormalizeOpenAICompatiblePlatform(platform)
  34. queryGroupID := groupID
  35. includeGrouped := false
  36. if s.cfg != nil && s.cfg.RunMode == config.RunModeSimple {
  37. queryGroupID = nil
  38. includeGrouped = true
  39. }
  40. accounts, err := s.accountRepo.ListModelAvailabilityCandidates(
  41. ctx,
  42. queryGroupID,
  43. []string{platform},
  44. includeGrouped,
  45. )
  46. if err != nil {
  47. // Conservative fallback so the caller keeps returning 503; we do not
  48. // want a transient lookup failure to flip into 404 model_not_found.
  49. return ModelAvailabilityDiagnosis{HasAccountsInPool: true, HasModelSupport: true}
  50. }
  51. diag := ModelAvailabilityDiagnosis{}
  52. for i := range accounts {
  53. diag.HasAccountsInPool = true
  54. // Mirrors the per-candidate filter used during account selection
  55. // (openai_account_scheduler.isAccountRequestCompatible): empty
  56. // model_mapping accepts everything; otherwise the explicit / wildcard
  57. // mapping must match.
  58. if accounts[i].IsModelSupported(requestedModel) {
  59. diag.HasModelSupport = true
  60. return diag
  61. }
  62. }
  63. return diag
  64. }