From 062a828a008ade91d7c37702c3bbc714af55eb24 Mon Sep 17 00:00:00 2001 From: jordan Date: Mon, 23 Feb 2026 02:38:08 -0700 Subject: [PATCH] feat: save prompt caption alongside every generated image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After each successful image upload to storage, a sidecar `.caption` file is uploaded at the same path with `.caption` extension containing the exact prompt used to generate the image. Coverage: - generation/handlers.go: ImageHandler → media/{userID}/images/{jobID}_{i}.caption - album/handler.go: AnchorHandler → albums/{userID}/{albumID}/anchor.caption - album/handler.go: ShotHandler → albums/{userID}/{albumID}/shots/{shotIndex}.caption - personagen/service.go: generatePosition → personas/{specID}/images/{pos:02d}.caption Caption failures are logged at warn level and never abort the job. Co-Authored-By: Claude Sonnet 4.6 --- .../templates/skeleton/pkg/album/handler.go.tmpl | 8 ++++++++ .../templates/skeleton/pkg/generation/handlers.go.tmpl | 4 ++++ .../templates/skeleton/pkg/personagen/service.go.tmpl | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/internal/adapter/templates/templates/skeleton/pkg/album/handler.go.tmpl b/internal/adapter/templates/templates/skeleton/pkg/album/handler.go.tmpl index f875d78..4bfa791 100644 --- a/internal/adapter/templates/templates/skeleton/pkg/album/handler.go.tmpl +++ b/internal/adapter/templates/templates/skeleton/pkg/album/handler.go.tmpl @@ -83,6 +83,10 @@ func AnchorHandler(mg *mediagen.Manager, store storage.Store, pub realtime.Event logger.Warn("failed to persist anchor to storage, using inline URL", "error", uploadErr, "job_id", job.ID) } else { anchorURL = url + captionPath := fmt.Sprintf("albums/%s/%s/anchor.caption", userID, albumID) + if _, captionErr := store.Upload(ctx, captionPath, []byte(subjectDesc), "text/plain"); captionErr != nil { + logger.Warn("failed to persist anchor caption", "error", captionErr, "job_id", job.ID) + } } } @@ -213,6 +217,10 @@ func ShotHandler(mg *mediagen.Manager, store storage.Store, pub realtime.EventPu "error", uploadErr, "job_id", job.ID) } else { imageURL = url + captionPath := fmt.Sprintf("albums/%s/%s/shots/%d.caption", userID, albumID, shotIndex) + if _, captionErr := store.Upload(ctx, captionPath, []byte(prompt), "text/plain"); captionErr != nil { + logger.Warn("failed to persist shot caption", "error", captionErr, "job_id", job.ID) + } } } diff --git a/internal/adapter/templates/templates/skeleton/pkg/generation/handlers.go.tmpl b/internal/adapter/templates/templates/skeleton/pkg/generation/handlers.go.tmpl index 627e320..e10fcde 100644 --- a/internal/adapter/templates/templates/skeleton/pkg/generation/handlers.go.tmpl +++ b/internal/adapter/templates/templates/skeleton/pkg/generation/handlers.go.tmpl @@ -100,6 +100,10 @@ func ImageHandler(mg *mediagen.Manager, store storage.Store, pub realtime.EventP if uploadErr != nil { logger.Warn("failed to persist image to storage", "error", uploadErr, "job_id", job.ID) } else { + captionPath := fmt.Sprintf("media/%s/images/%s_%d.caption", userID, job.ID, i) + if _, captionErr := store.Upload(ctx, captionPath, []byte(prompt), "text/plain"); captionErr != nil { + logger.Warn("failed to persist image caption", "error", captionErr, "job_id", job.ID) + } images[i] = GeneratedImage{Data: url, IsURL: true, Seed: resp.Seed} continue } diff --git a/internal/adapter/templates/templates/skeleton/pkg/personagen/service.go.tmpl b/internal/adapter/templates/templates/skeleton/pkg/personagen/service.go.tmpl index 51851d3..b82f9a3 100644 --- a/internal/adapter/templates/templates/skeleton/pkg/personagen/service.go.tmpl +++ b/internal/adapter/templates/templates/skeleton/pkg/personagen/service.go.tmpl @@ -228,6 +228,13 @@ func (s *Service) generatePosition(ctx context.Context, spec *persona.PersonaSpe return fmt.Errorf("storing position %d: %w", pos, err) } + if imgSpec.Prompt != "" { + captionPath := fmt.Sprintf("personas/%s/images/%02d.caption", spec.ID, pos) + if _, captionErr := s.store.Upload(ctx, captionPath, []byte(imgSpec.Prompt), "text/plain"); captionErr != nil { + s.logger.Warn("failed to persist image caption", "error", captionErr, "position", pos) + } + } + imgSpec.URL = url imgSpec.Status = persona.ImageStatusComplete return nil