fix: grok-image 补齐 aspect_ratio 与 resolution 传参 (#184)
* fix: grok-image 补齐 aspect_ratio 与 resolution 传参 - 能力配置开放比例与 1k/2k 分辨率 - 请求体映射 size→aspect_ratio、quality→resolution - 画布设置展示「分辨率」选项 - 补充单测 Closes #182 * fix(create): 创作页图片质量支持 1k/2k 分辨率选项 创作页原先硬编码 auto/low/medium/high,导致 grok 能力配置里的 1k/2k 无法显示;改为按模型 quality.values 动态渲染,有 1k/2k 时标题显示「分辨率」。
This commit is contained in:
1 parent
99cc880eb8
commit
1b0fd7fd49
8 files changed
+164
-15
No files matched your search
@@ -109,8 +109,9 @@ func DefaultImageCapabilityConfig(protocol string, modelName string) *ImageCapab
|
||||
case model.ChannelInterfaceGrokImage:
|
||||
image.References.MaxImages = 1
|
||||
image.References.MaskSupported = false
|
||||
image.Size = ImageSizeConfig{Parameter: "none", Values: []string{}, Default: "auto", AllowCustom: false}
|
||||
image.Quality = ImageQualityConfig{Supported: false, Values: []string{}, Default: "auto"}
|
||||
// grok2api / xAI Imagine:size→aspect_ratio,quality→resolution(1k/2k)。
|
||||
image.Size = ImageSizeConfig{Parameter: "aspect_ratio", Values: []string{"1:1", "3:4", "4:3", "9:16", "16:9", "2:3", "3:2"}, Default: "1:1", AllowCustom: false}
|
||||
image.Quality = ImageQualityConfig{Supported: true, Values: []string{"1k", "2k"}, Default: "2k"}
|
||||
image.TransparentBackground = VideoBooleanConfig{Supported: false, Default: false}
|
||||
image.ResponseFormat = ParameterSupport{Supported: true}
|
||||
image.OutputFormat = ParameterSupport{Supported: false}
|
||||
@@ -132,8 +133,8 @@ func DefaultImageCapabilityConfig(protocol string, modelName string) *ImageCapab
|
||||
if model.ChannelInterfaceType(protocol) != model.ChannelInterfaceGrokImage && strings.HasPrefix(strings.ToLower(strings.TrimSpace(modelName)), "grok-imagine-image") {
|
||||
image.References.MaxImages = 0
|
||||
image.References.MaskSupported = false
|
||||
image.Size = ImageSizeConfig{Parameter: "none", Values: []string{}, Default: "auto", AllowCustom: false}
|
||||
image.Quality = ImageQualityConfig{Supported: false, Values: []string{}, Default: "auto"}
|
||||
image.Size = ImageSizeConfig{Parameter: "aspect_ratio", Values: []string{"1:1", "3:4", "4:3", "9:16", "16:9", "2:3", "3:2"}, Default: "1:1", AllowCustom: false}
|
||||
image.Quality = ImageQualityConfig{Supported: true, Values: []string{"1k", "2k"}, Default: "2k"}
|
||||
image.TransparentBackground = VideoBooleanConfig{Supported: false, Default: false}
|
||||
image.ResponseFormat = ParameterSupport{Supported: true}
|
||||
image.OutputFormat = ParameterSupport{Supported: false}
|
||||
|
||||
@@ -746,7 +746,15 @@ func grokImageRequestBody(input canvasGenerationInput) (grokImageRequest, string
|
||||
if input.Mask != nil {
|
||||
return grokImageRequest{}, "", errors.New("Grok 图片协议不支持蒙版编辑,请移除蒙版后重试")
|
||||
}
|
||||
body := grokImageRequest{Model: input.Config.Model, Prompt: withSystemPrompt(input.Config, input.Prompt), N: 1, ResponseFormat: "url"}
|
||||
body := grokImageRequest{
|
||||
Model: input.Config.Model,
|
||||
Prompt: withSystemPrompt(input.Config, input.Prompt),
|
||||
N: 1,
|
||||
ResponseFormat: "url",
|
||||
Size: strings.TrimSpace(input.Config.Size),
|
||||
AspectRatio: normalizeGrokImageAspectRatio(input.Config.Size),
|
||||
Resolution: normalizeGrokImageResolution(input.Config.Quality),
|
||||
}
|
||||
if len(input.ReferenceImages) == 0 {
|
||||
return body, "/images/generations", nil
|
||||
}
|
||||
@@ -761,6 +769,63 @@ func grokImageRequestBody(input canvasGenerationInput) (grokImageRequest, string
|
||||
return body, "/images/edits", nil
|
||||
}
|
||||
|
||||
// normalizeGrokImageResolution 把画布 quality(1k/2k/high…)映射为 grok2api / xAI 的 resolution。
|
||||
func normalizeGrokImageResolution(quality string) string {
|
||||
raw := strings.ToLower(strings.TrimSpace(quality))
|
||||
switch raw {
|
||||
case "", "auto":
|
||||
return ""
|
||||
case "1k", "low", "standard":
|
||||
return "1k"
|
||||
case "2k", "medium", "hd", "high", "4k":
|
||||
// xAI Imagine 图片通常最高 2k;超出则夹到 2k,避免上游拒参。
|
||||
return "2k"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// normalizeGrokImageAspectRatio 把画布 size(如 1280x720 / 9:16)转成 grok2api / xAI 接受的 aspect_ratio。
|
||||
func normalizeGrokImageAspectRatio(size string) string {
|
||||
raw := strings.ToLower(strings.TrimSpace(strings.ReplaceAll(size, "×", "x")))
|
||||
if raw == "" || raw == "auto" {
|
||||
return ""
|
||||
}
|
||||
if strings.Contains(raw, ":") {
|
||||
switch raw {
|
||||
case "1:1", "3:4", "4:3", "9:16", "16:9", "2:3", "3:2", "9:19.5", "19.5:9", "1:2", "2:1":
|
||||
return raw
|
||||
}
|
||||
}
|
||||
parts := strings.Split(raw, "x")
|
||||
if len(parts) != 2 {
|
||||
return ""
|
||||
}
|
||||
w, wErr := strconv.Atoi(parts[0])
|
||||
h, hErr := strconv.Atoi(parts[1])
|
||||
if wErr != nil || hErr != nil || w <= 0 || h <= 0 {
|
||||
return ""
|
||||
}
|
||||
if w == h {
|
||||
return "1:1"
|
||||
}
|
||||
ratio := float64(w) / float64(h)
|
||||
switch {
|
||||
case w*9 == h*16 || (ratio >= 1.7 && ratio <= 1.8):
|
||||
return "16:9"
|
||||
case h*9 == w*16 || (ratio > 0 && ratio <= 1.0/1.7 && ratio >= 1.0/1.8):
|
||||
return "9:16"
|
||||
case w*3 == h*4 || (ratio > 1.2 && ratio < 1.4):
|
||||
return "4:3"
|
||||
case h*3 == w*4 || (ratio > 0.7 && ratio < 0.85):
|
||||
return "3:4"
|
||||
case w > h:
|
||||
return "16:9"
|
||||
default:
|
||||
return "9:16"
|
||||
}
|
||||
}
|
||||
|
||||
func grokImageInputURL(media providerMedia) (string, error) {
|
||||
if isPublicMediaURL(strings.TrimSpace(media.URL)) {
|
||||
return strings.TrimSpace(media.URL), nil
|
||||
|
||||
@@ -58,6 +58,10 @@ type grokImageRequest struct {
|
||||
Image *grokImageInput `json:"image,omitempty"`
|
||||
N int `json:"n"`
|
||||
ResponseFormat string `json:"response_format"`
|
||||
Size string `json:"size,omitempty"`
|
||||
AspectRatio string `json:"aspect_ratio,omitempty"`
|
||||
// Resolution 对应 xAI / grok2api 的 resolution(常见 1k / 2k)。
|
||||
Resolution string `json:"resolution,omitempty"`
|
||||
}
|
||||
|
||||
type grokImageInput struct {
|
||||
|
||||
@@ -196,6 +196,40 @@ func TestRunGrokImageTaskUsesJSONEditContract(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrokImageRequestBodyMapsAspectRatio(t *testing.T) {
|
||||
body, path, err := grokImageRequestBody(canvasGenerationInput{
|
||||
Prompt: "a cat",
|
||||
Config: providerConfig{Model: "grok-imagine-image", InterfaceType: "grok-image", Size: "9:16", Quality: "2k"},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("grokImageRequestBody() error = %v", err)
|
||||
}
|
||||
if path != "/images/generations" {
|
||||
t.Fatalf("path = %q", path)
|
||||
}
|
||||
if body.AspectRatio != "9:16" || body.Size != "9:16" || body.Resolution != "2k" {
|
||||
t.Fatalf("body = %#v", body)
|
||||
}
|
||||
if got := normalizeGrokImageAspectRatio("1280x720"); got != "16:9" {
|
||||
t.Fatalf("normalize 1280x720 = %q", got)
|
||||
}
|
||||
if got := normalizeGrokImageAspectRatio("720x1280"); got != "9:16" {
|
||||
t.Fatalf("normalize 720x1280 = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeGrokImageResolution(t *testing.T) {
|
||||
if got := normalizeGrokImageResolution("1k"); got != "1k" {
|
||||
t.Fatalf("1k = %q", got)
|
||||
}
|
||||
if got := normalizeGrokImageResolution("high"); got != "2k" {
|
||||
t.Fatalf("high = %q", got)
|
||||
}
|
||||
if got := normalizeGrokImageResolution("auto"); got != "" {
|
||||
t.Fatalf("auto = %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGrokImageRequestBodyRejectsMaskAndMultipleReferences(t *testing.T) {
|
||||
if _, _, err := grokImageRequestBody(canvasGenerationInput{Config: providerConfig{InterfaceType: "grok-image"}, Mask: &providerMedia{DataURL: testReferenceImageDataURL}}); err == nil || !strings.Contains(err.Error(), "不支持蒙版") {
|
||||
t.Fatalf("mask error = %v", err)
|
||||
|
||||
Reference in new issue
Block a user