-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
feat(inpainting): add inpainting endpoint, wire ImageGenerationFunc and return generated image URL #7328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
feat(inpainting): add inpainting endpoint, wire ImageGenerationFunc and return generated image URL #7328
Conversation
✅ Deploy Preview for localai ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
1a714e7 to
68bfe00
Compare
| // write JSON | ||
| enc := json.NewEncoder(jf) | ||
| if err := enc.Encode(jsonFile); err != nil { | ||
| jf.Close() |
Check warning
Code scanning / gosec
Errors unhandled
| enc := json.NewEncoder(jf) | ||
| if err := enc.Encode(jsonFile); err != nil { | ||
| jf.Close() | ||
| os.Remove(jf.Name()) |
Check warning
Code scanning / gosec
Errors unhandled
| os.Remove(jf.Name()) | ||
| return err | ||
| } | ||
| jf.Close() |
Check warning
Code scanning / gosec
Errors unhandled
| jf.Close() | ||
| // rename to desired name | ||
| if err := os.Rename(jf.Name(), jsonPath); err != nil { | ||
| os.Remove(jf.Name()) |
Check warning
Code scanning / gosec
Errors unhandled
| // prepare dst | ||
| outTmp, err := os.CreateTemp(tmpDir, "out_") | ||
| if err != nil { | ||
| os.Remove(jsonPath) |
Check warning
Code scanning / gosec
Errors unhandled
| os.Remove(jsonPath) | ||
| return err | ||
| } | ||
| outTmp.Close() |
Check warning
Code scanning / gosec
Errors unhandled
| outTmp.Close() | ||
| dst := outTmp.Name() + ".png" | ||
| if err := os.Rename(outTmp.Name(), dst); err != nil { | ||
| os.Remove(jsonPath) |
Check warning
Code scanning / gosec
Errors unhandled
| // Note: ImageGenerationFunc will call into the loaded model's GenerateImage which expects src JSON | ||
| fn, err := backend.ImageGenerationFunc(height, width, 0, steps, 0, prompt, "", jsonPath, dst, ml, *cfg, appConfig, nil) | ||
| if err != nil { | ||
| os.Remove(jsonPath) |
Check warning
Code scanning / gosec
Errors unhandled
|
|
||
| // Execute generation function (blocking) | ||
| if err := fn(); err != nil { | ||
| os.Remove(jsonPath) |
Check warning
Code scanning / gosec
Errors unhandled
| // Execute generation function (blocking) | ||
| if err := fn(); err != nil { | ||
| os.Remove(jsonPath) | ||
| os.Remove(dst) |
Check warning
Code scanning / gosec
Errors unhandled
68bfe00 to
1d33fbf
Compare
1d33fbf to
5884e9c
Compare
|
|
||
| // Call backend image generation via indirection so tests can stub it | ||
| // Note: ImageGenerationFunc will call into the loaded model's GenerateImage which expects src JSON | ||
| fn, err := backend.ImageGenerationFunc(height, width, 0, steps, 0, prompt, "", jsonPath, dst, ml, *cfg, appConfig, nil) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ImageGeneration actually take a list of refimages files. We could wire these up to the image and mask, so it becomes open to the backend to implement this correctly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi thanks for the pointer, good catch.
I wired the uploaded image and mask as reference image files and pass them to the backend via the refImages parameter so backends that expect file references can use them directly. For compatibility I kept the existing JSON src (base64) as well, and added robust best‑effort cleanup of all temp files. The inpainting endpoint now does:
- write image and mask to temp files (origRef, maskRef)
- write the src JSON (base64) as before (for backward compatibility)
- call backend.ImageGenerationFunc(..., refImages) with both paths
7e414a1 to
8aa0b43
Compare
- Created gallery/dreamshaper-8-inpainting.yaml with diffusers backend - Configured StableDiffusionInpaintPipeline for inpainting tasks - Added gallery index entry with model file mapping - Maps 'dreamshaper-8-inpainting' API name to DreamShaper_8_pruned.safetensors file - Fixes CI test failures: model file resolution now works via gallery system - Addresses maintainer feedback on PR mudler#7328 Signed-off-by: Greg <marianigregory@pm.me>
80af855 to
8bfe58a
Compare
79941c8 to
874b052
Compare
|
@mudler Can i do something for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR adds an OpenAI-compatible inpainting endpoint (/v1/images/inpainting) that accepts multipart form data containing an image, mask, and prompt to generate inpainted images. The implementation extends LocalAI's image generation capabilities by introducing a test-friendly indirection pattern for backend image generation.
Key Changes:
- New inpainting endpoint with multipart/form-data support for image and mask uploads
- Introduces
ImageGenerationFuncvariable inbackend/image.goto enable test stubbing - Comprehensive test coverage for both error cases and successful image generation
- Enhanced MCP logging to include model names for better debugging
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
core/http/endpoints/openai/inpainting.go |
New inpainting handler that processes multipart uploads, creates base64-encoded JSON payloads, manages temporary files with cleanup on error, and calls backend image generation |
core/http/endpoints/openai/inpainting_test.go |
Unit tests covering missing file validation and happy path with stubbed backend generation function |
core/http/routes/openai.go |
Registers inpainting routes (/v1/images/inpainting, /images/inpainting) with image middleware for model selection and configuration |
core/backend/image.go |
Adds ImageGenerationFunc variable pointing to ImageGeneration function to enable test stubbing |
core/http/endpoints/openai/mcp.go |
Enhances logging by adding model name (config.Name) to tool call and result log messages |
swagger/swagger.yaml |
Documents the new inpainting endpoint with all parameters (model, prompt, steps, image, mask) and response schema |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| b64Mask := base64.StdEncoding.EncodeToString(maskBytes) | ||
|
|
||
| // get model config from context (middleware set it) | ||
| cfg, ok := c.Get("MODEL_CONFIG").(*config.ModelConfig) |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the constant middleware.CONTEXT_LOCALS_KEY_MODEL_CONFIG instead of the hardcoded string "MODEL_CONFIG" for consistency with other endpoints (e.g., image.go line 77, chat.go line 193). This follows the codebase convention and makes refactoring safer.
| cfg, ok := c.Get("MODEL_CONFIG").(*config.ModelConfig) | |
| cfg, ok := c.Get(middleware.CONTEXT_LOCALS_KEY_MODEL_CONFIG).(*config.ModelConfig) |
| jsonName := fmt.Sprintf("inpaint_%s.json", id) | ||
| jsonPath := filepath.Join(tmpDir, jsonName) |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable jsonName is declared but never used. The code computes it at line 116 but then creates a temp file with os.CreateTemp (line 122) and renames that to jsonPath (line 211), making jsonName unnecessary. Consider removing this unused variable to improve code clarity.
| jsonName := fmt.Sprintf("inpaint_%s.json", id) | |
| jsonPath := filepath.Join(tmpDir, jsonName) | |
| jsonPath := filepath.Join(tmpDir, fmt.Sprintf("inpaint_%s.json", id)) |
| c := e.NewContext(reqBuf, rec) | ||
|
|
||
| // set a minimal model config in context as handler expects | ||
| c.Set("MODEL_CONFIG", &config.ModelConfig{Backend: "diffusers"}) |
Copilot
AI
Nov 23, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use the constant middleware.CONTEXT_LOCALS_KEY_MODEL_CONFIG instead of the hardcoded string "MODEL_CONFIG" for consistency with the rest of the codebase.
e356139 to
dd5ffe7
Compare
Signed-off-by: Greg <marianigregory@pm.me>
dd5ffe7 to
d45913b
Compare
Description
This patch implements an OpenAI-compatible inpainting endpoint and wires image generation into the LocalAI backend. Changes are limited to the LocalAI Go codebase.
Adds InpaintingEndpoint handling POST /v1/images/inpainting with multipart/form-data support for image and mask.
Endpoint writes a JSON payload with base64 image/mask and prepares a temporary output PNG path, then calls backend.ImageGenerationFunc(...) to perform generation.
Introduces and uses the test-friendly indirection ImageGenerationFunc in image.go so the generation logic can be stubbed in tests.
Adds unit tests for the inpainting handler (inpainting_test.go) covering missing-files and the happy path (with backend.ImageGenerationFunc stubbed).
Registers the inpainting routes and a default model name middleware in openai.go.
Updates Swagger (swagger.yaml) and adds a Swaggo-style docstring on the new endpoint for proper OpenAPI generation.
Notes for Reviewers
Signed commits