diff --git a/core/bifrost.go b/core/bifrost.go index 32959ed30a..6e42fe25e5 100644 --- a/core/bifrost.go +++ b/core/bifrost.go @@ -1248,8 +1248,12 @@ func (bifrost *Bifrost) ImageEditRequest(ctx *schemas.BifrostContext, req *schem }, } } - // Prompt is not required when type is background_removal - if (req.Params == nil || req.Params.Type == nil || *req.Params.Type != "background_removal") && + // Prompt is not required for certain operation types that work without a text prompt + var imageEditParamsType *string + if req.Params != nil { + imageEditParamsType = req.Params.Type + } + if !isPromptOptionalImageEditType(imageEditParamsType) && (req.Input == nil || req.Input.Prompt == "") && !isLargePayloadPassthrough(ctx) { return nil, &schemas.BifrostError{ IsBifrostError: false, @@ -1316,8 +1320,12 @@ func (bifrost *Bifrost) ImageEditStreamRequest(ctx *schemas.BifrostContext, req }, } } - // Prompt is not required when type is background_removal - if (req.Params == nil || req.Params.Type == nil || *req.Params.Type != "background_removal") && + // Prompt is not required for certain operation types that work without a text prompt + var imageEditStreamParamsType *string + if req.Params != nil { + imageEditStreamParamsType = req.Params.Type + } + if !isPromptOptionalImageEditType(imageEditStreamParamsType) && (req.Input == nil || req.Input.Prompt == "") && !isLargePayloadPassthrough(ctx) { return nil, &schemas.BifrostError{ IsBifrostError: false, diff --git a/core/providers/bedrock/bedrock.go b/core/providers/bedrock/bedrock.go index 9799c74ac6..9d8f09a246 100644 --- a/core/providers/bedrock/bedrock.go +++ b/core/providers/bedrock/bedrock.go @@ -1953,8 +1953,10 @@ func (provider *BedrockProvider) ImageGenerationStream(ctx *schemas.BifrostConte } // ImageEdit performs image editing using Amazon Bedrock. -// Supports Titan Image Generator v1, Nova Canvas v1, and Titan Image Generator v2. -// Supports three edit types: INPAINTING, OUTPAINTING, and BACKGROUND_REMOVAL. +// Supports Titan Image Generator v1, Nova Canvas v1, Titan Image Generator v2 (three edit types: +// INPAINTING, OUTPAINTING, BACKGROUND_REMOVAL), and Stability AI edit models (inpaint, outpaint, +// recolor, search-replace, erase-object, remove-bg, control-sketch, control-structure, style-guide, +// style-transfer, upscale-creative, upscale-conservative, upscale-fast). // Returns a BifrostImageGenerationResponse containing the edited images and any error that occurred. func (provider *BedrockProvider) ImageEdit(ctx *schemas.BifrostContext, key schemas.Key, request *schemas.BifrostImageEditRequest) (*schemas.BifrostImageGenerationResponse, *schemas.BifrostError) { if err := providerUtils.CheckOperationAllowed(schemas.Bedrock, provider.customProviderConfig, schemas.ImageEditRequest); err != nil { @@ -1969,17 +1971,25 @@ func (provider *BedrockProvider) ImageEdit(ctx *schemas.BifrostContext, key sche var jsonData []byte var bifrostError *schemas.BifrostError + // Resolve deployment alias before building the request body so that + // Stability AI routing and task-type inference use the actual model ID. + path, deployment := provider.getModelPath("invoke", request.Model, key) + jsonData, bifrostError = providerUtils.CheckContextAndGetRequestBody( ctx, request, - func() (providerUtils.RequestBodyWithExtraParams, error) { return ToBedrockImageEditRequest(request) }, + func() (providerUtils.RequestBodyWithExtraParams, error) { + if isStabilityAIModel(deployment) { + return ToStabilityAIImageEditRequest(request, deployment) + } + return ToBedrockImageEditRequest(request) + }, provider.GetProviderKey()) if bifrostError != nil { return nil, bifrostError } // Make API request (same URL as image generation) - path, deployment := provider.getModelPath("invoke", request.Model, key) rawResponse, latency, providerResponseHeaders, bifrostError := provider.completeRequest(ctx, jsonData, path, key) if providerResponseHeaders != nil { ctx.SetValue(schemas.BifrostContextKeyProviderResponseHeaders, providerResponseHeaders) diff --git a/core/providers/bedrock/images.go b/core/providers/bedrock/images.go index eba0f1f7ef..bbf9330a48 100644 --- a/core/providers/bedrock/images.go +++ b/core/providers/bedrock/images.go @@ -405,6 +405,292 @@ func buildImageGenerationConfig(params *schemas.ImageEditParameters) *ImageGener return config } +// getStabilityAITaskTypeFromParams maps the generic BifrostImageEditParameters.Type value +// to a Stability AI task type string. Returns "" if the value is not a recognized Stability AI task type. +func getStabilityAITaskTypeFromParams(t string) string { + switch strings.ToLower(t) { + case "inpainting", "inpaint": + return "inpaint" + case "outpainting", "outpaint": + return "outpaint" + case "background_removal", "remove_background": + return "remove-bg" + case "erase_object": + return "erase-object" + case "upscale_fast": + return "upscale-fast" + case "upscale_creative": + return "upscale-creative" + case "upscale_conservative": + return "upscale-conservative" + case "recolor": + return "recolor" + case "search_replace": + return "search-replace" + case "control_sketch": + return "control-sketch" + case "control_structure": + return "control-structure" + case "style_guide": + return "style-guide" + case "style_transfer": + return "style-transfer" + default: + return "" + } +} + +// getStabilityAIEditTaskType infers the Stability AI edit task from the model name. +// Returns an error if the model name does not match any known pattern. +func getStabilityAIEditTaskType(model string) (string, error) { + m := strings.ToLower(model) + switch { + case strings.Contains(m, "stable-creative-upscale"): + return "upscale-creative", nil + case strings.Contains(m, "stable-conservative-upscale"): + return "upscale-conservative", nil + case strings.Contains(m, "stable-fast-upscale"): + return "upscale-fast", nil + case strings.Contains(m, "stable-image-inpaint"): + return "inpaint", nil + case strings.Contains(m, "stable-outpaint"): + return "outpaint", nil + case strings.Contains(m, "stable-image-search-recolor"): + return "recolor", nil + case strings.Contains(m, "stable-image-search-replace"): + return "search-replace", nil + case strings.Contains(m, "stable-image-erase-object"): + return "erase-object", nil + case strings.Contains(m, "stable-image-remove-background"): + return "remove-bg", nil + case strings.Contains(m, "stable-image-control-sketch"): + return "control-sketch", nil + case strings.Contains(m, "stable-image-control-structure"): + return "control-structure", nil + case strings.Contains(m, "stable-image-style-guide"): + return "style-guide", nil + case strings.Contains(m, "stable-style-transfer"): + return "style-transfer", nil + default: + return "", fmt.Errorf("cannot determine task type from stability ai model name %q", model) + } +} + +// ToStabilityAIImageEditRequest converts a Bifrost image edit request to the Stability AI flat request +// format used by Bedrock edit models. Only fields valid for the detected task type are populated. +// deployment is the resolved model identifier (after applying any deployment alias mapping); it is +// used for task-type inference so that alias-mapped models route correctly. +func ToStabilityAIImageEditRequest(request *schemas.BifrostImageEditRequest, deployment string) (*StabilityAIImageEditRequest, error) { + if request == nil || request.Input == nil { + return nil, fmt.Errorf("request or input is nil") + } + + var taskType string + if request.Params != nil && request.Params.Type != nil { + taskType = getStabilityAITaskTypeFromParams(*request.Params.Type) + } + if taskType == "" { + var err error + taskType, err = getStabilityAIEditTaskType(deployment) + if err != nil { + return nil, err + } + } + + req := &StabilityAIImageEditRequest{} + + // Image sourcing + if taskType == "style-transfer" { + if len(request.Input.Images) != 2 { + return nil, fmt.Errorf("style-transfer requires exactly two images: init_image and style_image") + } + if len(request.Input.Images[0].Image) == 0 || len(request.Input.Images[1].Image) == 0 { + return nil, fmt.Errorf("style-transfer requires non-empty init_image and style_image") + } + initB64 := base64.StdEncoding.EncodeToString(request.Input.Images[0].Image) + styleB64 := base64.StdEncoding.EncodeToString(request.Input.Images[1].Image) + req.InitImage = &initB64 + req.StyleImage = &styleB64 + } else { + if len(request.Input.Images) == 0 || len(request.Input.Images[0].Image) == 0 { + return nil, fmt.Errorf("at least one image is required") + } + imageB64 := base64.StdEncoding.EncodeToString(request.Input.Images[0].Image) + req.Image = &imageB64 + } + + // Common fields populated based on task allowlist + prompt := request.Input.Prompt + switch taskType { + case "inpaint", "recolor", "search-replace", "control-sketch", "control-structure", + "style-guide", "upscale-creative", "upscale-conservative", "outpaint", "style-transfer": + req.Prompt = &prompt + } + + // Negative prompt + if request.Params != nil && request.Params.NegativePrompt != nil { + switch taskType { + case "inpaint", "outpaint", "recolor", "search-replace", "control-sketch", + "control-structure", "style-guide", "upscale-creative", "upscale-conservative", "style-transfer": + req.NegativePrompt = request.Params.NegativePrompt + } + } + + // Seed + if request.Params != nil && request.Params.Seed != nil { + switch taskType { + case "inpaint", "outpaint", "recolor", "search-replace", "erase-object", "control-sketch", + "control-structure", "style-guide", "upscale-creative", "upscale-conservative", "style-transfer": + req.Seed = request.Params.Seed + } + } + + // Mask (from Params.Mask bytes) + if request.Params != nil && len(request.Params.Mask) > 0 { + switch taskType { + case "inpaint", "erase-object": + maskB64 := base64.StdEncoding.EncodeToString(request.Params.Mask) + req.Mask = &maskB64 + } + } + + // ExtraParams + if request.Params != nil { + // Typed OutputFormat takes priority over ExtraParams + if request.Params.OutputFormat != nil { + req.OutputFormat = request.Params.OutputFormat + } + + if request.Params.ExtraParams != nil { + ep := make(map[string]interface{}, len(request.Params.ExtraParams)) + for k, v := range request.Params.ExtraParams { + ep[k] = v + } + + // output_format — all tasks (fallback if not already set by typed field) + if req.OutputFormat == nil { + if v, ok := schemas.SafeExtractStringPointer(ep["output_format"]); ok { + delete(ep, "output_format") + req.OutputFormat = v + } + } + + // style_preset + switch taskType { + case "inpaint", "outpaint", "recolor", "search-replace", "control-sketch", + "control-structure", "style-guide", "upscale-creative": + if v, ok := schemas.SafeExtractStringPointer(ep["style_preset"]); ok { + delete(ep, "style_preset") + req.StylePreset = v + } + } + + // grow_mask + switch taskType { + case "inpaint", "recolor", "search-replace", "erase-object": + if v, ok := schemas.SafeExtractIntPointer(ep["grow_mask"]); ok { + delete(ep, "grow_mask") + req.GrowMask = v + } + } + + // outpaint directional fields + if taskType == "outpaint" { + if v, ok := schemas.SafeExtractIntPointer(ep["left"]); ok { + delete(ep, "left") + req.Left = v + } + if v, ok := schemas.SafeExtractIntPointer(ep["right"]); ok { + delete(ep, "right") + req.Right = v + } + if v, ok := schemas.SafeExtractIntPointer(ep["up"]); ok { + delete(ep, "up") + req.Up = v + } + if v, ok := schemas.SafeExtractIntPointer(ep["down"]); ok { + delete(ep, "down") + req.Down = v + } + } + + // creativity + switch taskType { + case "upscale-creative", "upscale-conservative", "outpaint": + if v, ok := schemas.SafeExtractFloat64Pointer(ep["creativity"]); ok { + delete(ep, "creativity") + req.Creativity = v + } + } + + // select_prompt (recolor) + if taskType == "recolor" { + if v, ok := schemas.SafeExtractStringPointer(ep["select_prompt"]); ok { + delete(ep, "select_prompt") + req.SelectPrompt = v + } + } + + // search_prompt (search-replace) + if taskType == "search-replace" { + if v, ok := schemas.SafeExtractStringPointer(ep["search_prompt"]); ok { + delete(ep, "search_prompt") + req.SearchPrompt = v + } + } + + // control_strength + switch taskType { + case "control-sketch", "control-structure": + if v, ok := schemas.SafeExtractFloat64Pointer(ep["control_strength"]); ok { + delete(ep, "control_strength") + req.ControlStrength = v + } + } + + // style-guide fields + if taskType == "style-guide" { + if v, ok := schemas.SafeExtractStringPointer(ep["aspect_ratio"]); ok { + delete(ep, "aspect_ratio") + req.AspectRatio = v + } + if v, ok := schemas.SafeExtractFloat64Pointer(ep["fidelity"]); ok { + delete(ep, "fidelity") + req.Fidelity = v + } + } + + // style-transfer fields + if taskType == "style-transfer" { + if v, ok := schemas.SafeExtractFloat64Pointer(ep["style_strength"]); ok { + delete(ep, "style_strength") + req.StyleStrength = v + } + if v, ok := schemas.SafeExtractFloat64Pointer(ep["composition_fidelity"]); ok { + delete(ep, "composition_fidelity") + req.CompositionFidelity = v + } + if v, ok := schemas.SafeExtractFloat64Pointer(ep["change_strength"]); ok { + delete(ep, "change_strength") + req.ChangeStrength = v + } + } + + req.ExtraParams = ep + } + } + + // Validate required per-task fields + if taskType == "recolor" && (req.SelectPrompt == nil || *req.SelectPrompt == "") { + return nil, fmt.Errorf("select_prompt is required for stability ai recolor task") + } + if taskType == "search-replace" && (req.SearchPrompt == nil || *req.SearchPrompt == "") { + return nil, fmt.Errorf("search_prompt is required for stability ai search-replace task") + } + + return req, nil +} + // ToBifrostImageGenerationResponse converts a Bedrock image generation response to a Bifrost image generation response func ToBifrostImageGenerationResponse(response *BedrockImageGenerationResponse) *schemas.BifrostImageGenerationResponse { if response == nil { diff --git a/core/providers/bedrock/types.go b/core/providers/bedrock/types.go index 87468fe976..3950a79f9c 100644 --- a/core/providers/bedrock/types.go +++ b/core/providers/bedrock/types.go @@ -770,11 +770,63 @@ func (req *StabilityAIImageGenerationRequest) GetExtraParams() map[string]interf return req.ExtraParams } -// BedrockImageGenerationResponse represents a Bedrock image generation response +// StabilityAIImageEditRequest is the flat JSON body for Stability AI image-edit models on Bedrock. +// Only the fields valid for the detected task type are populated. +type StabilityAIImageEditRequest struct { + // Shared params + Image *string `json:"image,omitempty"` // base64, primary input image + Prompt *string `json:"prompt,omitempty"` + NegativePrompt *string `json:"negative_prompt,omitempty"` + Seed *int `json:"seed,omitempty"` + OutputFormat *string `json:"output_format,omitempty"` + StylePreset *string `json:"style_preset,omitempty"` + Mask *string `json:"mask,omitempty"` // base64 mask image + GrowMask *int `json:"grow_mask,omitempty"` + + // Outpaint + Left *int `json:"left,omitempty"` + Right *int `json:"right,omitempty"` + Up *int `json:"up,omitempty"` + Down *int `json:"down,omitempty"` + + // Upscale-creative / upscale-conservative / outpaint + Creativity *float64 `json:"creativity,omitempty"` + + // Recolor + SelectPrompt *string `json:"select_prompt,omitempty"` + + // Search-replace + SearchPrompt *string `json:"search_prompt,omitempty"` + + // Control-sketch / control-structure + ControlStrength *float64 `json:"control_strength,omitempty"` + + // Style-guide + AspectRatio *string `json:"aspect_ratio,omitempty"` + Fidelity *float64 `json:"fidelity,omitempty"` + + // Style-transfer (uses different image field names) + InitImage *string `json:"init_image,omitempty"` + StyleImage *string `json:"style_image,omitempty"` + StyleStrength *float64 `json:"style_strength,omitempty"` + CompositionFidelity *float64 `json:"composition_fidelity,omitempty"` + ChangeStrength *float64 `json:"change_strength,omitempty"` + + ExtraParams map[string]interface{} `json:"-"` +} + +func (req *StabilityAIImageEditRequest) GetExtraParams() map[string]interface{} { + return req.ExtraParams +} + +// BedrockImageGenerationResponse represents a Bedrock image generation response. +// The Seeds and FinishReasons fields are populated by Stability AI edit models only. type BedrockImageGenerationResponse struct { - Images []string `json:"images"` // list of Base64 encoded images - MaskImage string `json:"maskImage"` // Base64 encoded mask image (optional) - Error string `json:"error"` // error message (if present) + Images []string `json:"images"` // list of Base64 encoded images + MaskImage string `json:"maskImage"` // Base64 encoded mask image (optional) + Error string `json:"error"` // error message (if present) + Seeds []int `json:"seeds"` // Stability AI: seeds used per image + FinishReasons []*string `json:"finish_reasons"` // Stability AI: finish reason per image (may be null) } // ==================== MODELS TYPES ==================== diff --git a/core/schemas/images.go b/core/schemas/images.go index d16df42a10..7f5ded7d7c 100644 --- a/core/schemas/images.go +++ b/core/schemas/images.go @@ -254,7 +254,7 @@ type ImageInput struct { } type ImageEditParameters struct { - Type *string `json:"type,omitempty"` // "inpainting", "outpainting", "background_removal", + Type *string `json:"type,omitempty"` // "inpainting", "outpainting", "background_removal", "remove_background", "erase_object", "recolor", "search_replace", "control_sketch", "control_structure", "style_guide", "style_transfer", "upscale_fast", "upscale_creative", "upscale_conservative" Background *string `json:"background,omitempty"` // "transparent", "opaque", "auto" InputFidelity *string `json:"input_fidelity,omitempty"` // "low", "high" Mask []byte `json:"mask,omitempty"` diff --git a/core/utils.go b/core/utils.go index dfbc7a95b6..8e926c94a7 100644 --- a/core/utils.go +++ b/core/utils.go @@ -10,6 +10,7 @@ import ( "math/rand" "net" "net/url" + "slices" "strings" "time" @@ -508,3 +509,17 @@ func buildSessionKey(providerKey schemas.ModelProvider, sessionID string, model } return "session:" + string(providerKey) + ":" + hashedSessionID + ":" + hashSHA256(discriminator) } + +// isPromptOptionalImageEditType returns true for edit task types that do not require a text prompt. +// It normalises hyphenated variants (e.g. "erase-object") to underscore form before matching. +func isPromptOptionalImageEditType(t *string) bool { + if t == nil { + return false + } + normalized := strings.ToLower(strings.TrimSpace(*t)) + normalized = strings.ReplaceAll(normalized, "-", "_") + return slices.Contains( + []string{"background_removal", "remove_background", "remove_bg", "erase_object", "upscale_fast"}, + normalized, + ) +} diff --git a/docs/providers/supported-providers/bedrock.mdx b/docs/providers/supported-providers/bedrock.mdx index 47492afd84..f3a288541c 100644 --- a/docs/providers/supported-providers/bedrock.mdx +++ b/docs/providers/supported-providers/bedrock.mdx @@ -26,6 +26,7 @@ AWS Bedrock supports multiple model families (Claude, Nova, Mistral, Llama, Cohe | **Llama** | ✅ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | | **Cohere** | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | | **Titan** | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | +| **Stability AI** | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ### Supported Operations @@ -761,6 +762,62 @@ resp, err := client.ImageGenerationRequest(schemas.NewBifrostContext(ctx, schema +## Stability AI models + +Supported generation models: **`stability.stable-image-core-v1:1`**, **`stability.stable-image-ultra-v1:1`** + +These models use a flat JSON body (not the nested Bedrock `taskType` structure). Bifrost detects them automatically — any model ID containing `"stability."` is converted via `ToStabilityAIImageGenerationRequest`. + +**Request Parameters** + +| Parameter | Type | Required | Notes | +|-----------|------|----------|-------| +| `prompt` | string | ✅ | Text description of the image | +| `negative_prompt` | string | ❌ | What to exclude | +| `seed` | int | ❌ | Reproducibility seed | +| `aspect_ratio` | string | ❌ | e.g. `"16:9"`, `"1:1"`, `"21:9"` — via `aspect_ratio` param or `ExtraParams["aspect_ratio"]` | +| `output_format` | string | ❌ | `"png"`, `"jpeg"`, `"webp"` — via `output_format` param | + +### Example Request + + + + +```bash +curl -X POST http://localhost:8080/v1/images/generations \ + -H "Content-Type: application/json" \ + -d '{ + "model": "bedrock/us.stability.stable-image-ultra-v1:1", + "prompt": "A majestic mountain range at golden hour", + "negative_prompt": "blurry, low quality", + "aspect_ratio": "16:9", + "output_format": "png", + "seed": 42 + }' +``` + + + + +```go +resp, err := client.ImageGenerationRequest(schemas.NewBifrostContext(ctx, schemas.NoDeadline), &schemas.BifrostImageGenerationRequest{ + Provider: schemas.Bedrock, + Model: "us.stability.stable-image-ultra-v1:1", + Input: &schemas.ImageGenerationInput{ + Prompt: "A majestic mountain range at golden hour", + }, + Params: &schemas.ImageGenerationParameters{ + NegativePrompt: schemas.Ptr("blurry, low quality"), + AspectRatio: schemas.Ptr("16:9"), + OutputFormat: schemas.Ptr("png"), + Seed: schemas.Ptr(42), + }, +}) +``` + + + + --- # 6. Image Edit @@ -840,6 +897,176 @@ Bedrock supports three image edit task types: **INPAINTING**, **OUTPAINTING**, a --- +## Stability AI models + + +Requests use **multipart/form-data**, not JSON. + + +Stability AI edit models are automatically detected by their model ID (contains `"stability."`). The **task type is inferred from the model name** by default, but you can also set the `type` field explicitly — useful when using deployment aliases. See [Type values for explicit task selection](#type-values-for-explicit-task-selection) below. + +### Supported models + +| Model ID | Task | Images required | Prompt | +|----------|------|-----------------|--------| +| `stability.stable-image-inpaint-v1:0` | inpaint | 1 + mask | ✅ | +| `stability.stable-outpaint-v1:0` | outpaint | 1 (optional mask) | ✅ | +| `stability.stable-image-search-recolor-v1:0` | recolor | 1 | ✅ | +| `stability.stable-image-search-replace-v1:0` | search-replace | 1 | ✅ | +| `stability.stable-image-erase-object-v1:0` | erase-object | 1 + mask | ❌ | +| `stability.stable-image-remove-background-v1:0` | remove-bg | 1 | ❌ | +| `stability.stable-image-control-sketch-v1:0` | control-sketch | 1 | ✅ | +| `stability.stable-image-control-structure-v1:0` | control-structure | 1 | ✅ | +| `stability.stable-image-style-guide-v1:0` | style-guide | 1 | ✅ | +| `stability.stable-style-transfer-v1:0` | style-transfer | **2 required** | ✅ | +| `stability.stable-creative-upscale-v1:0` | upscale-creative | 1 | ✅ | +| `stability.stable-conservative-upscale-v1:0` | upscale-conservative | 1 | ✅ | +| `stability.stable-fast-upscale-v1:0` | upscale-fast | 1 | ❌ | + +### Common parameters + +| Parameter | Type | Required | Notes | +|-----------|------|----------|-------| +| `model` | string | ✅ | Stability AI model ID (see table above) | +| `image[]` | binary | ✅ | Input image(s). `style-transfer` requires exactly 2. | +| `prompt` | string | task-dependent | Required for all tasks except `remove-bg`, `upscale-fast`, and `erase-object`. For these no-prompt operations, set `type` to `remove_background`, `upscale_fast`, or `erase_object` to skip prompt validation at the gateway level. | +| `negative_prompt` | string | ❌ | Not applied for: `remove-bg`, `upscale-fast`, `erase-object` | +| `seed` | int | ❌ | Not applied for: `remove-bg`, `upscale-fast` | +| `mask` | binary | task-dependent | Required for: `inpaint`, `erase-object`; ignored for others | + +### Task-specific extra parameters + +Pass these via `extra_params` (Go SDK) or as top-level form fields (Gateway). + +| Extra parameter | Type | Task(s) | +|-----------------|------|---------| +| `output_format` | string | All — `"png"`, `"jpeg"`, `"webp"` | +| `style_preset` | string | inpaint, outpaint, recolor, search-replace, control-sketch, control-structure, style-guide, upscale-creative | +| `grow_mask` | int | inpaint, recolor, search-replace, erase-object | +| `left`, `right`, `up`, `down` | int | outpaint — pixels to expand in each direction | +| `creativity` | float | upscale-creative, upscale-conservative, outpaint | +| `select_prompt` | string | recolor — which region to recolor | +| `search_prompt` | string | search-replace — what object to find and replace | +| `control_strength` | float | control-sketch, control-structure — 0.0–1.0 | +| `aspect_ratio` | string | style-guide — output aspect ratio | +| `fidelity` | float | style-guide — 0.0–1.0 | +| `style_strength` | float | style-transfer — 0.0–1.0 | +| `composition_fidelity` | float | style-transfer — 0.0–1.0 | +| `change_strength` | float | style-transfer — 0.0–1.0 | + + +**Style-transfer image order matters.** The first image (`image[0]`) becomes `init_image` (the content to transform) and the second (`image[1]`) becomes `style_image` (the artistic reference). Both images must be non-empty. + + +### Type values for explicit task selection + +You can set the `type` field to override model-name inference. This is especially useful with deployment aliases where the alias name may not contain the Stability AI model pattern. + +| `type` value | Stability AI task | +|---|---| +| `inpainting` or `inpaint` | inpaint | +| `outpainting` or `outpaint` | outpaint | +| `background_removal` or `remove_background` or `remove_bg` | remove-bg | +| `erase_object` | erase-object | +| `upscale_fast` | upscale-fast | +| `upscale_creative` | upscale-creative | +| `upscale_conservative` | upscale-conservative | +| `recolor` | recolor | +| `search_replace` | search-replace | +| `control_sketch` | control-sketch | +| `control_structure` | control-structure | +| `style_guide` | style-guide | +| `style_transfer` | style-transfer | + +### Example requests + + + + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-image-inpaint-v1:0" \ + -F "image[]=@photo.png;type=image/png" \ + -F "mask=@mask.png;type=image/png" \ + -F "prompt=A beautiful garden replacing the masked area" \ + -F "negative_prompt=ugly, blurry" \ + -F "output_format=png" +``` + + + + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-style-transfer-v1:0" \ + -F "image[]=@content.png;type=image/png" \ + -F "image[]=@style.png;type=image/png" \ + -F "prompt=Apply the artistic style to the content image" \ + -F "style_strength=0.8" \ + -F "composition_fidelity=0.5" \ + -F "change_strength=0.4" \ + -F "output_format=png" +``` + + + + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-outpaint-v1:0" \ + -F "image[]=@photo.png;type=image/png" \ + -F "prompt=Extend the scene naturally" \ + -F "left=200" \ + -F "right=200" \ + -F "output_format=png" +``` + + + + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-image-remove-background-v1:0" \ + -F "image[]=@photo.png;type=image/png" \ + -F "output_format=png" +``` + + + + +No prompt required. Set `type=erase_object` to skip gateway prompt validation. + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-image-erase-object-v1:0" \ + -F "image[]=@photo.png;type=image/png" \ + -F "mask=@mask.png;type=image/png" \ + -F "type=erase_object" \ + -F "output_format=png" +``` + + + + +No prompt required. Set `type=upscale_fast` to skip gateway prompt validation. + +```bash +curl -X POST http://localhost:8080/v1/images/edits \ + -F "model=bedrock/us.stability.stable-fast-upscale-v1:0" \ + -F "image[]=@photo.png;type=image/png" \ + -F "type=upscale_fast" \ + -F "output_format=png" +``` + + + + +**Endpoint**: `invoke` endpoint (same as all other Bedrock image operations) + +**Streaming**: Not supported. + +--- + # 7. Image Variation diff --git a/transports/bifrost-http/handlers/inference.go b/transports/bifrost-http/handlers/inference.go index d88d56ebb4..733403bcf9 100644 --- a/transports/bifrost-http/handlers/inference.go +++ b/transports/bifrost-http/handlers/inference.go @@ -1836,11 +1836,6 @@ func prepareImageEditRequest(ctx *fasthttp.RequestCtx) (*ImageEditHTTPRequest, * editType = typeValues[0] } promptValues := form.Value["prompt"] - if editType != "background_removal" { - if len(promptValues) == 0 || promptValues[0] == "" { - return nil, nil, fmt.Errorf("prompt is required") - } - } var imageFiles []*multipart.FileHeader if imageFilesArray := form.File["image[]"]; len(imageFilesArray) > 0 { imageFiles = imageFilesArray