feat: support image quality control

This commit is contained in:
2026-05-28 00:17:27 +08:00
parent 1d3a4f1da9
commit f5c69b9852
9 changed files with 243 additions and 9 deletions
+2 -1
View File
@@ -214,7 +214,7 @@ type ImageGenerationRequest struct {
Prompt string `json:"prompt" example:"A watercolor robot reading a book"`
N int `json:"n,omitempty" example:"1"`
Size string `json:"size,omitempty" example:"1024x1024"`
Quality string `json:"quality,omitempty" example:"standard"`
Quality string `json:"quality,omitempty" example:"auto"`
ResponseFormat string `json:"response_format,omitempty" example:"url"`
RunMode string `json:"runMode,omitempty" example:"simulation"`
}
@@ -226,6 +226,7 @@ type ImageEditRequest struct {
Mask string `json:"mask,omitempty" example:"https://example.com/mask.png"`
N int `json:"n,omitempty" example:"1"`
Size string `json:"size,omitempty" example:"1024x1024"`
Quality string `json:"quality,omitempty" example:"auto"`
ResponseFormat string `json:"response_format,omitempty" example:"url"`
RunMode string `json:"runMode,omitempty" example:"simulation"`
}
@@ -63,6 +63,7 @@ func NewParamProcessorChain() ParamProcessorChain {
durationProcessor{},
audioProcessor{},
imageCountProcessor{},
imageQualityProcessor{},
},
}
}
@@ -378,3 +378,64 @@ func (imageCountProcessor) Process(params map[string]any, modelType string, cont
params["n"] = count
return true
}
type imageQualityProcessor struct{}
func (imageQualityProcessor) Name() string { return "ImageQualityProcessor" }
var openAICompatibleImageQualities = map[string]struct{}{
"low": {},
"medium": {},
"high": {},
"auto": {},
}
func (imageQualityProcessor) ShouldProcess(params map[string]any, modelType string, context *paramProcessContext) bool {
if modelType != "image_generate" && modelType != "image_edit" {
return false
}
_, ok := params["quality"]
return ok
}
func (imageQualityProcessor) Process(params map[string]any, modelType string, context *paramProcessContext) bool {
capability := capabilityForType(context.modelCapability, modelType)
quality := stringFromAny(params["quality"])
if supportsImageQualityControl(capability) && isOpenAICompatibleImageQuality(quality) {
return true
}
before := params["quality"]
delete(params, "quality")
context.recordChange(
"ImageQualityProcessor",
"remove",
"quality",
before,
nil,
"模型能力未开启生成质量控制,已移除 quality 参数。",
capabilityPath(modelType, "support_quality_control"),
capabilityValue(context.modelCapability, modelType, "support_quality_control"),
)
return true
}
func supportsImageQualityControl(capability map[string]any) bool {
if capability == nil {
return false
}
for _, key := range []string{"support_quality_control", "supportQualityControl", "quality_control", "qualityControl", "quality"} {
if boolFromAny(capability[key]) {
return true
}
}
return false
}
func isOpenAICompatibleImageQuality(value string) bool {
if value == "" {
return false
}
_, ok := openAICompatibleImageQualities[value]
return ok
}
@@ -660,3 +660,56 @@ func TestParamProcessorImageResolutionAndOutputCount(t *testing.T) {
t.Fatalf("image count should be capped to 4, got %+v", processed["n"])
}
}
func TestParamProcessorImageQualityControl(t *testing.T) {
body := map[string]any{
"model": "mock-image",
"prompt": "draw",
"quality": "high",
}
unsupported := preprocessRequestWithLog("images.generations", body, store.RuntimeModelCandidate{
ModelType: "image_generate",
Capabilities: map[string]any{
"image_generate": map[string]any{
"output_resolutions": []any{"1K"},
},
},
})
if _, ok := unsupported.Body["quality"]; ok {
t.Fatalf("quality should be removed when capability does not support it: %+v", unsupported.Body)
}
if len(unsupported.Log.Changes) == 0 || unsupported.Log.Changes[len(unsupported.Log.Changes)-1].CapabilityPath != "capabilities.image_generate.support_quality_control" {
t.Fatalf("expected quality removal to be logged against support_quality_control, got %+v", unsupported.Log.Changes)
}
supported := preprocessRequest("images.generations", body, store.RuntimeModelCandidate{
ModelType: "image_generate",
Capabilities: map[string]any{
"image_generate": map[string]any{
"support_quality_control": true,
"output_resolutions": []any{"1K"},
},
},
})
if supported["quality"] != "high" {
t.Fatalf("quality should be retained when capability supports it: %+v", supported)
}
incompatible := preprocessRequest("images.generations", map[string]any{
"model": "mock-image",
"prompt": "draw",
"quality": "standard",
}, store.RuntimeModelCandidate{
ModelType: "image_generate",
Capabilities: map[string]any{
"image_generate": map[string]any{
"support_quality_control": true,
"output_resolutions": []any{"1K"},
},
},
})
if _, ok := incompatible["quality"]; ok {
t.Fatalf("OpenAI-compatible GPT image quality should reject standard: %+v", incompatible)
}
}