Route direct image-edit requests through the img2img pipeline

Direct `models/infer` image requests were defaulting to `image.text2image`, which dropped reference inputs and produced unrelated generations. Route image-edit payloads to `image.img2img`, accept canonical `image` URLs during reference resolution, and preserve reference-aware prompt hints.

Constraint: The test server must continue accepting both canonical `image` and legacy alias fields from callers already in circulation
Rejected: Fix only reference URL parsing | direct overrides would still be misrouted as text2image before reference resolution runs
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Any future image-model route inference must inspect input shape before defaulting to text2image
Tested: `env GOCACHE=/tmp/popiartserver-gocache go test ./internal/server -run 'TestInferRouteKeyForModelRecognizesViduAsVideo|TestInferRouteKeyForModelRecognizesImageEditInputs|TestResolveImageToImageReferenceAcceptsCanonicalImageURL|TestGenerateEditedImageRefsUsesSeedreamImagesGenerationsEndpoint'`; deployed to `101.42.99.35` and verified Seedream gateway requests include `image`
Not-tested: Full manual validation against every non-Seedream image-edit provider
This commit is contained in:
wtgoku
2026-04-13 17:08:07 +08:00
parent 214ffa1dfc
commit a73b85c45f
3 changed files with 69 additions and 4 deletions
+14 -3
View File
@@ -1046,7 +1046,7 @@ func (s *Server) handleModelsInfer(w http.ResponseWriter, r *http.Request) {
writeError(w, http.StatusBadRequest, "VALIDATION_ERROR", "model_id is required", nil)
return
}
routeKey := inferRouteKeyForModel(req.ModelID)
routeKey := inferRouteKeyForModel(req.ModelID, req.Input)
record, statusCode, err := s.store.createJob(
"",
routeKey,
@@ -1334,10 +1334,20 @@ func (s *Server) resolveModelID(routeKey, projectID string) string {
}
}
func inferRouteKeyForModel(modelID string) string {
func inferRouteKeyForModel(modelID string, input map[string]any) string {
if modelType, _ := classifyModelIDFallback(modelID); modelType == "video" {
return "video.image2video"
}
if input != nil {
if ref := strings.TrimSpace(stringValue(
input["source_artifact_id"],
input["image"],
input["reference_image_url"],
input["image_url"],
)); ref != "" {
return "image.img2img"
}
}
return "image.text2image"
}
@@ -1371,6 +1381,7 @@ func (s *Server) resolveImageToImageReference(ctx context.Context, record *job,
}
refURL := strings.TrimSpace(stringValue(
input["image"],
input["reference_image_url"],
input["image_url"],
))
@@ -1540,7 +1551,7 @@ func buildGenericImageToImagePrompt(input map[string]any, prompt string) string
if prompt != "" {
parts = append(parts, prompt)
}
if refURL := strings.TrimSpace(stringValue(input["reference_image_url"], input["image_url"])); refURL != "" {
if refURL := strings.TrimSpace(stringValue(input["image"], input["reference_image_url"], input["image_url"])); refURL != "" {
parts = append(parts, "preserve the same main subject identity and key visual traits from the reference image")
}
if aspectRatio := strings.TrimSpace(stringValue(input["aspect_ratio"])); aspectRatio != "" {