img2img through the jobs queue — restyle from flexsiebels (#11 follow-up) #13
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
#11 added img2img/restyle to the Backend contract + the
imagenCLI. But flexsiebels does not use the CLI — it talks to ImaGen purely through theimagen.jobsqueue (write path from #8): it inserts a job row via Supabase (website/src/routes/api/admin/imagine/jobs/+server.ts→.from('jobs').insert(...)), theimagen workeron mRiver claims it, generates, writesimagen.images+ Storage, setsimage_id.That queue has no img2img columns, and
worker.Job/ the pipeline have no init-image path. So restyle is unreachable from flexsiebels (m's main frontend) until the queue learns it. This issue plumbs img2img through the queue so flexsiebels (and any other queue producer) can enqueue a restyle.Schema (migration — ImaGen owns the
imagenschema)All additions nullable → backward-compatible with existing txt2img inserts (flexsiebels' current inserts keep working untouched).
imagen.jobs:init_image_id uuid NULL REFERENCES imagen.images(id)— the source image to condition on (the natural flexsiebels case: "restyle this image I'm looking at", which is already animagen.imagesrow).strength real NULL— transformation amount in [0,1]; maps tobackend.Request.Strength. Default at the producer; pipeline treats NULL as the backend/CLI default (0.6).imagen.images:source_image_id uuid NULL REFERENCES imagen.images(id)— lineage: a restyle result points at its source. Enables #6's "fork from this image" and a viewer before/after.Deliver the migration as a SQL file in the repo's migration location (match how #7 created the
imagenschema). The head applies it to Supabase at merge time (irreversible infra action) viaapply_migration— do not apply it yourself; commit the SQL + note it in the done report.Worker + pipeline
internal/worker/worker.goJobstruct: addInitImageID string,Strength float64. Zero values = txt2img (same convention as today).Queueclaim query (incmd/imagen): select the two new columns intoJob.Run(the implementation behind thePipelineinterface): whenInitImageID != "":imagen.imagesrow; fetch its bytes from Storage viastorage_path(extendinternal/cloudwith a download — it already has the Storage client + bucket from #7).backend.Request{InitImage: &backend.ImageInput{Data, MimeType, Filename}, Strength}(Strength = job.Strength, or the 0.6 default when 0).source_image_id = init_image_idand recordmode=img2img+strength+source_image_idin the sidecar jsonb (the sidecar writer already serialises Result.Metadata).ImageInputCapable/SupportsImageInput()is false,MarkFailedwith a clear error ("backend X does not support image input") — never silently fall back to txt2img. (#11 already gives the sentinelErrImageInputUnsupported.)Tests (house rules: next to packages, no network)
worker_test.go:Jobround-tripsInitImageID/Strength; pipeline img2img branch with a fake Storage fetch + mock backend; capability-failure path →MarkFailedwith the right message; txt2img path unchanged whenInitImageIDempty.Out of scope
Refs
imagen.images), #6 (viewer fork-from-image), #12 (live smoke)website/src/routes/api/admin/imagine/jobs/+server.ts,lib/server/imagen.tsshift-1 (hephaestus) — #13 implemented end-to-end
Commit
2904ac2onmai/hephaestus/img2img-through-the-jobs.go build ./...,go vet ./...,go test ./...all clean.Migration (committed UNAPPLIED — head applies at merge)
migrations/imagen_img2img_lineage.sql:imagen.jobs+=init_image_id uuid NULL REFERENCES imagen.images(id) ON DELETE SET NULL,strength real NULL CHECK (0..1)imagen.images+=source_image_id uuid NULL REFERENCES imagen.images(id) ON DELETE SET NULL+ partial lineage indeximagen_images_source_idximagen_jobs_init/imagen_series_init.apply_migrationat merge time.Worker + pipeline
worker.Job+=InitImageID string,Strength float64(zero values = txt2img).COALESCE(init_image_id::text,''),COALESCE(strength,0)).internal/cloud.FetchImage(imageID): resolvesstorage_pathvia PostgREST (imagen profile) then downloads the object from the singleimagen-generatedbucket (per cronus' coordinated decision — no bucket param; user-uploaded/URL sources land underinputs/...in the same bucket). MIME sniffed when the Storage header is missing/octet-stream.SyncRequest.SourceImageIDwritten to the result row.Runimg2img branch: capability check → fetch source bytes →Request.InitImage+Strength(NULL strength → 0.6 default, matching #11's CLI/backend default) → dispatch (comfyui img2img path from #11) → stampssource_image_id+mode+strengthontoResult.Metadata(lands in disk sidecar +imagen.images.sidecarjsonb) and setssource_image_idcolumn.ImageInputCapable/ reportsSupportsImageInput()==false→ hardMarkFailed("backend X does not support image input"), never a silent txt2img fallback.Tests (next to packages, no network)
internal/worker/worker_test.go: Job round-tripsInitImageID/Strength; txt2img path leaves them zero.cmd/imagen/worker_img2img_test.go: capability-failure → failed Outcome with the right message;resolveInitImagefetch success + error; full Run buildsRequest.InitImage+default strength and records lineage metadata; explicit strength preserved.internal/cloud/cloud_test.go:FetchImagehappy path (bucket-relative URL + imagen profile header), MIME-sniff fallback, not-found;SourceImageIDwritten/omitted in the insert body.cmd/imagen/worker_integration_test.go: env-guarded real-pgx test that the claim query scansinit_image_id+strengthintoworker.Job.Out of scope (untouched, per issue)
flexsiebels UI (paul), ControlNet/Redux, Replicate img2img.
Merged to main (
933b2e2) and migration applied to Supabase.What landed:
migrations/imagen_img2img_lineage.sqlapplied — verified live:imagen.jobs.init_image_id(uuid),imagen.jobs.strength(real, CHECK 0..1),imagen.images.source_image_id(uuid) + partial index. All nullable, so existing txt2img producers are unaffected.worker.JobcarriesInitImageID/Strength; pgx claim query selects them.internal/cloud.FetchImageresolvesstorage_pathby image id and downloads from the singleimagen-generatedbucket.ImageInputCapable— never a silent text-to-image fallback. Strength NULL → 0.6. Recordssource_image_id+mode+strengthlineage on the result row + sidecar.go build/vet/test ./...clean.Remaining to go live: the
imagen-workeron mRiver must be redeployed with this binary (the old process keeps running and safely ignores the new columns, but won't process restyle jobs until restarted). After that + flexsiebels'ENABLE_RESTYLEflag, restyle is live end-to-end.Contract is now open for the flexsiebels producer (init_image_id + strength on the jobs insert). Implemented by hephaestus.