grok_media_image_geometry.go 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. package service
  2. import (
  3. "math"
  4. "strconv"
  5. "strings"
  6. "github.com/tidwall/gjson"
  7. "github.com/tidwall/sjson"
  8. )
  9. // Official Imagine image geometry: https://docs.x.ai/developers/model-capabilities/images/generation
  10. var grokImagineAspectRatioValues = []struct {
  11. label string
  12. ratio float64
  13. }{
  14. {"1:1", 1},
  15. {"16:9", 16.0 / 9.0},
  16. {"9:16", 9.0 / 16.0},
  17. {"4:3", 4.0 / 3.0},
  18. {"3:4", 3.0 / 4.0},
  19. {"3:2", 1.5},
  20. {"2:3", 2.0 / 3.0},
  21. {"2:1", 2},
  22. {"1:2", 0.5},
  23. {"19.5:9", 19.5 / 9.0},
  24. {"9:19.5", 9.0 / 19.5},
  25. {"20:9", 20.0 / 9.0},
  26. {"9:20", 9.0 / 20.0},
  27. }
  28. func applyGrokImagineImageGeometry(body []byte) ([]byte, error) {
  29. size := strings.TrimSpace(gjson.GetBytes(body, "size").String())
  30. resolution := grokImagineImageResolution(gjson.GetBytes(body, "resolution").String())
  31. aspect := strings.TrimSpace(gjson.GetBytes(body, "aspect_ratio").String())
  32. out := append([]byte(nil), body...)
  33. if resolution == "" {
  34. if derived := grokImagineImageResolutionFromSize(size); derived != "" {
  35. next, err := sjson.SetBytes(out, "resolution", derived)
  36. if err != nil {
  37. return nil, err
  38. }
  39. out = next
  40. }
  41. } else if gjson.GetBytes(body, "resolution").String() != resolution {
  42. next, err := sjson.SetBytes(out, "resolution", resolution)
  43. if err != nil {
  44. return nil, err
  45. }
  46. out = next
  47. }
  48. if aspect == "" {
  49. if derived := grokImagineAspectRatioFromSize(size); derived != "" {
  50. next, err := sjson.SetBytes(out, "aspect_ratio", derived)
  51. if err != nil {
  52. return nil, err
  53. }
  54. out = next
  55. }
  56. }
  57. if !gjson.GetBytes(out, "size").Exists() {
  58. return out, nil
  59. }
  60. return sjson.DeleteBytes(out, "size")
  61. }
  62. func assignGrokMediaResolution(value string, info *GrokMediaRequestInfo) {
  63. if info == nil {
  64. return
  65. }
  66. value = strings.TrimSpace(value)
  67. if value == "" {
  68. return
  69. }
  70. if img := grokImagineImageResolution(value); img != "" {
  71. info.ImageResolution = img
  72. return
  73. }
  74. info.Resolution = value
  75. }
  76. func grokImagineImageResolution(value string) string {
  77. switch strings.ToLower(strings.TrimSpace(value)) {
  78. case "1k":
  79. return "1k"
  80. case "2k":
  81. return "2k"
  82. default:
  83. return ""
  84. }
  85. }
  86. func grokImagineImageResolutionFromSize(size string) string {
  87. if explicit := grokImagineImageResolution(size); explicit != "" {
  88. return explicit
  89. }
  90. tier, ok := ClassifyImageBillingTier(size)
  91. if !ok {
  92. return ""
  93. }
  94. if tier == ImageBillingSize1K {
  95. return "1k"
  96. }
  97. return "2k"
  98. }
  99. func grokImagineAspectRatioFromSize(size string) string {
  100. width, height, ok := parseImageBillingDimensions(strings.TrimSpace(size))
  101. if !ok || width <= 0 || height <= 0 {
  102. return ""
  103. }
  104. div := grokImagineGCD(width, height)
  105. exact := strconv.Itoa(width/div) + ":" + strconv.Itoa(height/div)
  106. for _, candidate := range grokImagineAspectRatioValues {
  107. if candidate.label == exact {
  108. return exact
  109. }
  110. }
  111. ratio := float64(width) / float64(height)
  112. bestLabel := ""
  113. bestDelta := math.MaxFloat64
  114. for _, candidate := range grokImagineAspectRatioValues {
  115. delta := math.Abs(ratio - candidate.ratio)
  116. if delta < bestDelta {
  117. bestDelta = delta
  118. bestLabel = candidate.label
  119. }
  120. }
  121. return bestLabel
  122. }
  123. func grokImagineGCD(a, b int) int {
  124. if a < 0 {
  125. a = -a
  126. }
  127. if b < 0 {
  128. b = -b
  129. }
  130. for b != 0 {
  131. a, b = b, a%b
  132. }
  133. if a == 0 {
  134. return 1
  135. }
  136. return a
  137. }