batch_image_provider.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. package service
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "strings"
  8. "time"
  9. "github.com/Wei-Shaw/sub2api/internal/config"
  10. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  11. )
  12. type BatchImageProvider interface {
  13. Name() string
  14. SupportsAccount(account *Account) bool
  15. Submit(ctx context.Context, job *BatchImageJob, account *Account, input BatchImageInput) (*BatchProviderJob, error)
  16. Get(ctx context.Context, job *BatchImageJob, account *Account) (*BatchProviderStatus, error)
  17. Cancel(ctx context.Context, job *BatchImageJob, account *Account) error
  18. OpenResult(ctx context.Context, job *BatchImageJob, account *Account) (io.ReadCloser, string, error)
  19. Cleanup(ctx context.Context, job *BatchImageJob, account *Account, target CleanupTarget) error
  20. }
  21. type BatchImageProviderRegistry struct {
  22. providers map[string]BatchImageProvider
  23. }
  24. func NewBatchImageProviderRegistry(providers ...BatchImageProvider) *BatchImageProviderRegistry {
  25. r := &BatchImageProviderRegistry{providers: make(map[string]BatchImageProvider, len(providers))}
  26. for _, provider := range providers {
  27. if provider == nil || strings.TrimSpace(provider.Name()) == "" {
  28. continue
  29. }
  30. r.providers[provider.Name()] = provider
  31. }
  32. return r
  33. }
  34. func NewDefaultBatchImageProviderRegistry() *BatchImageProviderRegistry {
  35. return NewBatchImageProviderRegistry(
  36. NewGeminiAPIBatchImageProvider(nil),
  37. NewVertexBatchImageProvider(VertexBatchImageProviderOptions{}, nil, nil, nil),
  38. )
  39. }
  40. func NewBatchImageProviderRegistryFromConfig(cfg *config.Config) *BatchImageProviderRegistry {
  41. return NewBatchImageProviderRegistry(
  42. NewGeminiAPIBatchImageProvider(nil),
  43. NewVertexBatchImageProviderFromConfig(cfg, nil, nil, nil),
  44. )
  45. }
  46. func (r *BatchImageProviderRegistry) Get(provider string) (BatchImageProvider, bool) {
  47. if r == nil {
  48. return nil, false
  49. }
  50. p, ok := r.providers[provider]
  51. return p, ok
  52. }
  53. func (r *BatchImageProviderRegistry) MustGet(provider string) (BatchImageProvider, error) {
  54. p, ok := r.Get(provider)
  55. if !ok {
  56. return nil, ErrBatchImageInvalidProvider
  57. }
  58. return p, nil
  59. }
  60. type BatchImageInput struct {
  61. BatchID string
  62. Model string
  63. DisplayName string
  64. Items []BatchImageInputItem
  65. ResponseMimeType string
  66. AspectRatio string
  67. ImageSize string
  68. Metadata map[string]string
  69. }
  70. type BatchImageInputItem struct {
  71. CustomID string
  72. Prompt string
  73. ReferenceImages []BatchImageReference
  74. }
  75. type BatchImageReference struct {
  76. ID string
  77. Type string
  78. MimeType string
  79. Data []byte
  80. FileURI string
  81. }
  82. type BatchProviderJob struct {
  83. ProviderJobName string
  84. ProviderInputRef string
  85. ProviderOutputRef string
  86. RawState string
  87. }
  88. type BatchProviderInternalState string
  89. const (
  90. BatchProviderStateQueued BatchProviderInternalState = "queued"
  91. BatchProviderStateRunning BatchProviderInternalState = "running"
  92. BatchProviderStateSucceeded BatchProviderInternalState = "succeeded"
  93. BatchProviderStateFailed BatchProviderInternalState = "failed"
  94. BatchProviderStateCancelled BatchProviderInternalState = "cancelled"
  95. BatchProviderStateExpired BatchProviderInternalState = "expired"
  96. )
  97. type BatchProviderStatus struct {
  98. RawState string
  99. InternalState BatchProviderInternalState
  100. Done bool
  101. ProviderOutputRef string
  102. ErrorCode string
  103. ErrorMessage string
  104. SuggestedRequeueAfter time.Duration
  105. }
  106. type CleanupTarget string
  107. const (
  108. CleanupTargetInput CleanupTarget = "input"
  109. CleanupTargetOutput CleanupTarget = "output"
  110. CleanupTargetAll CleanupTarget = "all"
  111. )
  112. var (
  113. ErrBatchImageProviderUnsupportedAccount = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_UNSUPPORTED_ACCOUNT", "batch image provider does not support this account")
  114. ErrBatchImageProviderMissingAPIKey = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_MISSING_API_KEY", "batch image provider account is missing api key")
  115. ErrBatchImageProviderMissingServiceAccount = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_MISSING_SERVICE_ACCOUNT", "batch image provider account is missing service account credentials")
  116. ErrBatchImageProviderMissingJobName = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_MISSING_JOB_NAME", "batch image provider job name is missing")
  117. ErrBatchImageProviderMissingResultRef = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_MISSING_RESULT_REF", "batch image provider result reference is missing")
  118. ErrBatchImageProviderInlineResultUnsupported = infraerrors.New(http.StatusBadRequest, "GEMINI_INLINE_BATCH_RESULT_UNSUPPORTED", "Gemini inline batch result is not supported")
  119. ErrBatchImageProviderInvalidInput = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_INVALID_INPUT", "invalid batch image provider input")
  120. ErrBatchImageProviderUnsafeCleanupPath = infraerrors.New(http.StatusBadRequest, "VERTEX_UNSAFE_CLEANUP_PATH", "unsafe batch image cleanup path")
  121. ErrUnsupportedCleanupTarget = infraerrors.New(http.StatusBadRequest, "BATCH_IMAGE_PROVIDER_UNSUPPORTED_CLEANUP_TARGET", "unsupported batch image cleanup target")
  122. )
  123. func batchImageProviderJobName(job *BatchImageJob) string {
  124. if job == nil || job.ProviderJobName == nil {
  125. return ""
  126. }
  127. return strings.TrimSpace(*job.ProviderJobName)
  128. }
  129. func batchImageProviderInputRef(job *BatchImageJob) string {
  130. if job == nil || job.ProviderInputRef == nil {
  131. return ""
  132. }
  133. return strings.TrimSpace(*job.ProviderInputRef)
  134. }
  135. func batchImageProviderOutputRef(job *BatchImageJob) string {
  136. if job == nil || job.ProviderOutputRef == nil {
  137. return ""
  138. }
  139. return strings.TrimSpace(*job.ProviderOutputRef)
  140. }
  141. func batchImageProviderAPIKey(account *Account) string {
  142. if account == nil {
  143. return ""
  144. }
  145. return strings.TrimSpace(account.GetCredential("api_key"))
  146. }
  147. func batchImageProviderInputError(format string, args ...any) error {
  148. return ErrBatchImageProviderInvalidInput.WithCause(fmt.Errorf(format, args...))
  149. }