diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..46c8ed0 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1 @@ +test_output/ diff --git a/backend/internal/service/inference.go b/backend/internal/service/inference.go index ac9c896..bd55b0c 100644 --- a/backend/internal/service/inference.go +++ b/backend/internal/service/inference.go @@ -36,7 +36,6 @@ type imageGenRequest struct { Prompt string `json:"prompt"` N int `json:"n,omitempty"` Size string `json:"size,omitempty"` - ResponseFormat string `json:"response_format,omitempty"` Steps int `json:"steps,omitempty"` CFGScale float64 `json:"cfg_scale,omitempty"` } @@ -57,7 +56,12 @@ func GenerateImages(ctx context.Context, prompt string, params AssetParams) ([]G } if imgCfg.APIKey != "" { - return callImageGenAPI(ctx, prompt, count) + width, height := imgCfg.Width, imgCfg.Height + if params.Resolution > 0 { + width = params.Resolution + height = params.Resolution + } + return callImageGenAPI(ctx, prompt, count, width, height) } size := params.Resolution @@ -69,13 +73,12 @@ func GenerateImages(ctx context.Context, prompt string, params AssetParams) ([]G } // callImageGenAPI 调用 OpenAI 兼容的 Images API,返回生成的图片。 -func callImageGenAPI(ctx context.Context, prompt string, count int) ([]GeneratedImage, error) { +func callImageGenAPI(ctx context.Context, prompt string, count, width, height int) ([]GeneratedImage, error) { reqBody := imageGenRequest{ Model: imgCfg.Model, Prompt: prompt, N: count, - Size: fmt.Sprintf("%dx%d", imgCfg.Width, imgCfg.Height), - ResponseFormat: "b64_json", + Size: fmt.Sprintf("%dx%d", width, height), } if imgCfg.Steps > 0 { reqBody.Steps = imgCfg.Steps @@ -108,7 +111,7 @@ func callImageGenAPI(ctx context.Context, prompt string, count int) ([]Generated return nil, fmt.Errorf("image gen api error %d: %s", resp.StatusCode, string(b)) } - return parseImageResponse(ctx, resp.Body) + return parseImageResponse(ctx, resp.Body, width, height) } // ======================== 图片编辑 API ======================== @@ -138,7 +141,6 @@ func callImageEditAPI(ctx context.Context, imageData []byte, prompt string, coun writer.WriteField("model", imgCfg.Model) writer.WriteField("n", strconv.Itoa(count)) writer.WriteField("size", fmt.Sprintf("%dx%d", imgCfg.Width, imgCfg.Height)) - writer.WriteField("response_format", "b64_json") if err := writer.Close(); err != nil { return nil, fmt.Errorf("close multipart writer: %w", err) @@ -164,11 +166,11 @@ func callImageEditAPI(ctx context.Context, imageData []byte, prompt string, coun return nil, fmt.Errorf("image edit api error %d: %s", resp.StatusCode, string(b)) } - return parseImageResponse(ctx, resp.Body) + return parseImageResponse(ctx, resp.Body, imgCfg.Width, imgCfg.Height) } // parseImageResponse 解析 OpenAI 兼容的图片生成/编辑响应体。 -func parseImageResponse(ctx context.Context, r io.Reader) ([]GeneratedImage, error) { +func parseImageResponse(ctx context.Context, r io.Reader, width, height int) ([]GeneratedImage, error) { var genResp imageGenResponse if err := json.NewDecoder(r).Decode(&genResp); err != nil { return nil, fmt.Errorf("decode response: %w", err) @@ -195,8 +197,8 @@ func parseImageResponse(ctx context.Context, r io.Reader) ([]GeneratedImage, err } images = append(images, GeneratedImage{ Data: data, - Width: imgCfg.Width, - Height: imgCfg.Height, + Width: width, + Height: height, Format: "png", }) } diff --git a/backend/tools/gentest.go b/backend/tools/gentest.go new file mode 100644 index 0000000..d6af462 --- /dev/null +++ b/backend/tools/gentest.go @@ -0,0 +1,49 @@ +//go:build ignore + +package main + +import ( + "context" + "fmt" + "os" + + "gen2d/internal/config" + "gen2d/internal/service" +) + +func main() { + cfg := config.Load() + + // 清空 API key 强制走 mock + cfg.ImageGen.APIKey = "" + service.InitImageGenConfig(cfg.ImageGen) + service.InitLLMConfig(cfg.LLM) + + in := service.PipelineInput{ + AssetType: "sprite", + Prompt: "a cute cat warrior with golden armor", + Tags: []string{"pixel art", "fantasy", "16-bit"}, + Params: service.AssetParams{ + Resolution: 816, + Frames: service.FrameParams{Directions: 4, FramesPerDirection: 1}, + Format: "individual", + }, + } + + output, err := service.RunPipeline(context.Background(), in) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + os.MkdirAll("test_output", 0755) + for i, a := range output.Assets { + fname := fmt.Sprintf("test_output/gen_%d.%s", i, a.Format) + if err := os.WriteFile(fname, a.Data, 0644); err != nil { + fmt.Fprintf(os.Stderr, "write %s: %v\n", fname, err) + os.Exit(1) + } + fmt.Printf("saved %s (%d bytes)\n", fname, len(a.Data)) + } + fmt.Printf("metadata: %+v\n", output.Metadata) +}