fix(image): 图片生成 - 打通分辨率选择并补充回归测试

This commit is contained in:
ddcat666 committed 2026-10-14 09:34:41 +08:00
1 parent 6a118ae93b
commit db8a301474
9 files changed
+125 -20

No files matched your search

@@ -516,6 +516,57 @@ func TestParameterizedOpenAIAspectPresetSystemChannelPersistsPixelExecutionSize(
}
}
func TestParameterizedOpenAIPixelSizeSelectionAcceptsDeclaredSizeAndResolution(t *testing.T) {
profile := DefaultModelCapabilityConfigForModel(string(model.ChannelInterfaceOpenAIImage), "GPT Image 2.5")
minCount, maxCount := 1.0, 5.0
profile.Image.Parameters = []protocol.ImageParameter{
{
Name: "size", Label: "画幅 / 尺寸", Type: "string",
Values: []any{"auto", "1824x1024", "2752x1536", "3840x2160"}, Default: "1824x1024",
Pricing: true, Binding: "aspectRatio", Encoding: "pixels",
},
{
Name: "imageResolution", Label: "分辨率", Type: "string",
Values: []any{"1k", "2k", "4k"}, Default: "1k", Pricing: true, Binding: "resolution",
},
{Name: "count", Label: "生成数量", Type: "integer", Min: &minCount, Max: &maxCount, Default: 1, Pricing: true, Binding: "imageCount"},
{Name: "transparentBackground", Label: "透明背景", Type: "boolean", Default: false, Binding: "transparentBackground"},
}
profile.Image.Size = ImageSizeConfig{
Parameter: "size", Values: []string{"auto", "1824x1024", "2752x1536", "3840x2160"}, Default: "1824x1024",
Presets: []ImageSizePreset{
{Tier: "1k", Ratio: "16:9", Size: "1824x1024", Width: 1824, Height: 1024},
{Tier: "2k", Ratio: "16:9", Size: "2752x1536", Width: 2752, Height: 1536},
{Tier: "4k", Ratio: "16:9", Size: "3840x2160", Width: 3840, Height: 2160},
},
}
if err := projectImageParameters(profile.Image); err != nil {
t.Fatal(err)
}
svc, _, channel, channelModel := createSystemChannelSelectionFixture(t, "image", model.ChannelInterfaceOpenAIImage, profile, []model.ChannelModelPriceTier{
newSelectionPriceTier("tier-1k", `{"imageResolution":"1k"}`, "provider-1k", "fixed_request"),
newSelectionPriceTier("tier-2k", `{"imageResolution":"2k"}`, "provider-2k", "fixed_request"),
newSelectionPriceTier("tier-4k", `{"imageResolution":"4k"}`, "provider-4k", "fixed_request"),
})
input := map[string]any{
"mode": "image",
"config": map[string]any{
"channelId": channel.ID, "model": channelModel.ModelKey,
"size": "1824x1024", "imageResolution": "1k",
"imageParameters": map[string]any{"size": "1824x1024", "imageResolution": "1k", "count": 1, "transparentBackground": false},
},
}
resolved, err := svc.resolveSystemChannelModelSelection(input, "canvas_image", "")
if err != nil {
t.Fatalf("resolve error = %v", err)
}
config := resolved["config"].(map[string]any)
parameters := config["imageParameters"].(map[string]any)
if parameters["size"] != "1824x1024" || parameters["imageResolution"] != "1k" || config["priceTierId"] != "tier-1k" {
t.Fatalf("resolved parameters = %#v, config = %#v", parameters, config)
}
}
func TestOpenAIAspectPresetSystemChannelPersistsPixelExecutionSize(t *testing.T) {
profile := DefaultModelCapabilityConfigForModel(string(model.ChannelInterfaceOpenAIImage), "gpt-image-2.5-4k")
profile.Image.Size = ImageSizeConfig{
+4
View File
@@ -356,12 +356,16 @@ func publicWorkbenchParameters(kind string, snapshot, input map[string]any) map[
if kind == model.WorkbenchKindImage {
copyWorkbenchTextParameter(result, "size", parameters["size"])
copyWorkbenchTextParameter(result, "quality", parameters["quality"])
copyWorkbenchTextParameter(result, "imageResolution", parameters["imageResolution"])
if result["size"] == nil {
copyWorkbenchTextParameter(result, "size", config["size"])
}
if result["quality"] == nil {
copyWorkbenchTextParameter(result, "quality", config["quality"])
}
if result["imageResolution"] == nil {
copyWorkbenchTextParameter(result, "imageResolution", config["imageResolution"])
}
if value, ok := workbenchInteger(parameters["count"], 20); ok {
result["count"] = value
} else if value, ok := workbenchInteger(config["count"], 20); ok {
+16
View File
@@ -59,6 +59,22 @@ func TestTaskOutputIncludesOnlySafeWorkbenchRestoreData(t *testing.T) {
}
}
func TestTaskOutputRestoresWorkbenchImageResolution(t *testing.T) {
task := model.Task{InputJSON: `{"mode":"image","metadata":{"source":"workbench","workbenchKind":"image","workbench":{"kind":"image","parameters":{"size":"1824x1024","imageResolution":"2k","quality":"auto","count":1}}},"config":{"size":"1824x1024","imageResolution":"2k","quality":"auto","count":"1"}}`}
var output map[string]any
if err := json.Unmarshal([]byte(publicTaskInputJSON(task.InputJSON)), &output); err != nil {
t.Fatal(err)
}
workbench, ok := output["workbench"].(map[string]any)
if !ok {
t.Fatalf("workbench restore data missing: %#v", output)
}
parameters, ok := workbench["parameters"].(map[string]any)
if !ok || parameters["imageResolution"] != "2k" {
t.Fatalf("image resolution not preserved for restore: %#v", workbench)
}
}
func TestTaskMediaPreviewUsesSafeMediaURLs(t *testing.T) {
previewURL, previewKind := taskMediaPreview(`{"images":["data:image/png;base64,AAAA","/api/resources/resource-1/file"],"video":"https://cdn.example.com/output.mp4"}`, "video")
if previewURL != "/api/resources/resource-1/file" || previewKind != "image" {
+1 -1
View File
@@ -152,7 +152,7 @@ func (s *Service) WorkbenchTasks(userID, kind string, activeOnly bool, limit int
func (s *Service) resolveWorkbenchProviderConfig(kind, modelName, logicalModelID, channelID string, parameters map[string]any, input map[string]any) (map[string]any, error) {
config := map[string]any{"mode": kind}
if kind == WorkbenchKindImage {
for _, key := range []string{"size", "quality", "count"} {
for _, key := range []string{"size", "quality", "imageResolution", "count"} {
if value, exists := parameters[key]; exists {
config[key] = providerConfigOptionValue(value)
}
@@ -6,12 +6,12 @@ func TestResolveWorkbenchProviderConfigMapsWorkbenchParameters(t *testing.T) {
service := &Service{}
image, err := service.resolveWorkbenchProviderConfig(WorkbenchKindImage, "image-model", "logical-image", "", map[string]any{
"size": "1024x1024", "quality": "high", "count": 2,
"size": "1024x1024", "quality": "high", "imageResolution": "2k", "count": 2,
}, map[string]any{})
if err != nil {
t.Fatal(err)
}
for key, want := range map[string]any{"model": "image-model", "size": "1024x1024", "quality": "high", "count": "2"} {
for key, want := range map[string]any{"model": "image-model", "size": "1024x1024", "quality": "high", "imageResolution": "2k", "count": "2"} {
if image[key] != want {
t.Errorf("image config[%q] = %#v, want %#v", key, image[key], want)
}
+3 -2
View File
@@ -158,7 +158,7 @@ func TestWorkbenchTaskRejectsUnknownParametersAndClientConfig(t *testing.T) {
func TestWorkbenchTaskCanBeCreatedWithoutTool(t *testing.T) {
s, _, _ := newWorkbenchTest(t)
request := TaskInput{Kind: KindImage, Prompt: "A glass greenhouse at sunrise", Model: "image-model", LogicalModelID: "logical", Parameters: map[string]any{"size": "1024x1024", "count": 1}}
request := TaskInput{Kind: KindImage, Prompt: "A glass greenhouse at sunrise", Model: "image-model", LogicalModelID: "logical", Parameters: map[string]any{"size": "1024x1024", "imageResolution": "2k", "count": 1}}
resolved, err := s.ResolveTask("user", request)
if err != nil {
t.Fatal(err)
@@ -174,7 +174,8 @@ func TestWorkbenchTaskCanBeCreatedWithoutTool(t *testing.T) {
t.Fatalf("tool-less task contains a tool id: %#v", metadata)
}
snapshot := metadata["workbench"].(map[string]any)
if snapshot["kind"] != KindImage || snapshot["parameters"].(map[string]any)["count"] != 1 {
parameters := snapshot["parameters"].(map[string]any)
if snapshot["kind"] != KindImage || parameters["count"] != 1 || parameters["imageResolution"] != "2k" {
t.Fatalf("workbench parameters not snapshotted: %#v", snapshot)
}
if resolved.TaskType != "canvas_image" || resolved.Operation != "image" {
+2 -2
View File
@@ -194,7 +194,7 @@ func (s *Service) ResolveTask(userID string, req TaskInput) (*TaskResolution, er
func normalizeTaskParameters(kind string, params map[string]any) (map[string]any, error) {
allowed := map[string]map[string]bool{
KindImage: {"size": true, "quality": true, "count": true},
KindImage: {"size": true, "quality": true, "imageResolution": true, "count": true},
KindVideo: {"seconds": true, "ratio": true, "resolution": true, "generateAudio": true, "watermark": true},
}[kind]
if allowed == nil {
@@ -206,7 +206,7 @@ func normalizeTaskParameters(kind string, params map[string]any) (map[string]any
return nil, kernel.BadAuthRequest("生成参数包含不支持字段: " + key)
}
switch key {
case "size", "quality", "ratio", "resolution":
case "size", "quality", "imageResolution", "ratio", "resolution":
text, ok := value.(string)
text = strings.TrimSpace(text)
if !ok || len(text) > 64 || strings.Contains(text, "://") || strings.HasPrefix(strings.ToLower(text), "data:") {