summaryrefslogtreecommitdiff
path: root/internal/image
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-04-03 13:22:19 +0300
committerPaul Buetow <paul@buetow.org>2026-04-03 13:22:19 +0300
commit59f615cdb0a0a30b702ab810013351bd97fdc408 (patch)
tree3f7498986c9605166851b554d9bba5ce3d923352 /internal/image
parentb819475635eec77c72ab1b3ec0793dd59f750013 (diff)
feat: single tall comic strip with consistent characters and more dramatic narration
- Replace 3 separate comic pages with one 9:16 tall comic_strip.png - Prompt explicitly requests 3 vertically stacked panels with consistent characters - Add AspectRatio field to SearchOptions; NanoBanana uses it when set - Make cinematic narration instruction more dramatic (movie trailer narrator style) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'internal/image')
-rw-r--r--internal/image/nanobanana.go21
-rw-r--r--internal/image/nanobanana_test.go14
-rw-r--r--internal/image/search.go1
3 files changed, 23 insertions, 13 deletions
diff --git a/internal/image/nanobanana.go b/internal/image/nanobanana.go
index 4e99dfa..8bcd945 100644
--- a/internal/image/nanobanana.go
+++ b/internal/image/nanobanana.go
@@ -56,8 +56,8 @@ var newNanoBananaClient = genai.NewClient
var nanoBananaGenerateText = func(ctx context.Context, c *NanoBananaClient, model, systemPrompt, userPrompt string, temperature float32, maxOutputTokens int32) (string, error) {
return c.generateText(ctx, model, systemPrompt, userPrompt, temperature, maxOutputTokens)
}
-var nanoBananaGenerateImage = func(ctx context.Context, c *NanoBananaClient, prompt string) ([]byte, string, error) {
- return c.generateImage(ctx, prompt)
+var nanoBananaGenerateImage = func(ctx context.Context, c *NanoBananaClient, prompt, aspectRatio string) ([]byte, string, error) {
+ return c.generateImage(ctx, prompt, aspectRatio)
}
// NewNanoBananaClient creates a new Nano Banana client.
@@ -105,10 +105,16 @@ func (c *NanoBananaClient) Search(ctx context.Context, opts *SearchOptions) ([]S
c.PromptCallback(prompt)
}
+ // Resolve aspect ratio: use caller override if provided, else the default.
+ aspectRatio := nanoBananaAspectRatio
+ if opts.AspectRatio != "" {
+ aspectRatio = opts.AspectRatio
+ }
+
fmt.Printf("Nano Banana Image Generation Prompt (%d chars): %s\n", len(prompt), prompt)
- fmt.Printf("Nano Banana Image Generation: Using model '%s' with aspect ratio '%s'\n", c.modelName(), nanoBananaAspectRatio)
+ fmt.Printf("Nano Banana Image Generation: Using model '%s' with aspect ratio '%s'\n", c.modelName(), aspectRatio)
- imageBytes, mimeType, err := nanoBananaGenerateImage(ctx, c, prompt)
+ imageBytes, mimeType, err := nanoBananaGenerateImage(ctx, c, prompt, aspectRatio)
if err != nil {
if searchErr, ok := err.(*SearchError); ok {
return nil, searchErr
@@ -380,11 +386,14 @@ func (c *NanoBananaClient) generateText(ctx context.Context, model, systemPrompt
return text, nil
}
-func (c *NanoBananaClient) generateImage(ctx context.Context, prompt string) ([]byte, string, error) {
+func (c *NanoBananaClient) generateImage(ctx context.Context, prompt, aspectRatio string) ([]byte, string, error) {
+ if aspectRatio == "" {
+ aspectRatio = nanoBananaAspectRatio
+ }
cfg := &genai.GenerateContentConfig{
ResponseModalities: []string{string(genai.ModalityImage)},
ImageConfig: &genai.ImageConfig{
- AspectRatio: nanoBananaAspectRatio,
+ AspectRatio: aspectRatio,
},
}
diff --git a/internal/image/nanobanana_test.go b/internal/image/nanobanana_test.go
index 88cc43c..c9cfb61 100644
--- a/internal/image/nanobanana_test.go
+++ b/internal/image/nanobanana_test.go
@@ -71,7 +71,7 @@ func TestNanoBananaClient_Search_CustomPromptSkipsTextGeneration(t *testing.T) {
}
var gotPrompt string
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) {
gotPrompt = prompt
return mustPNGBytes(t), "image/png", nil
}
@@ -149,7 +149,7 @@ func TestNanoBananaClient_Search_GeneratedPromptFlow(t *testing.T) {
return "", nil
}
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) {
gotPrompt = prompt
return mustJPEGBytes(t), "image/jpeg", nil
}
@@ -249,7 +249,7 @@ func TestNanoBananaClient_Search_TranslationFailureFallsBackToQuery(t *testing.T
return "", nil
}
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) {
return mustPNGBytes(t), "image/png", nil
}
@@ -297,7 +297,7 @@ func TestNanoBananaClient_Search_TrivialSceneFallsBackToSubjectPrompt(t *testing
}
var gotPrompt string
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, prompt, _ string) ([]byte, string, error) {
gotPrompt = prompt
return mustPNGBytes(t), "image/png", nil
}
@@ -342,7 +342,7 @@ func TestNanoBananaClient_Search_ImageGenerationError(t *testing.T) {
return "", nil
}
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) {
return nil, "", fmt.Errorf("image generation failed")
}
@@ -376,7 +376,7 @@ func TestNanoBananaClient_Search_CustomPromptPreservesTranslationMetadata(t *tes
t.Fatal("unexpected text generation for custom prompt")
return "", nil
}
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) {
return mustPNGBytes(t), "image/png", nil
}
@@ -409,7 +409,7 @@ func TestNanoBananaClient_Search_CustomPromptIgnoresWhitespaceTranslation(t *tes
t.Fatal("unexpected text generation for custom prompt")
return "", nil
}
- nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _ string) ([]byte, string, error) {
+ nanoBananaGenerateImage = func(_ context.Context, _ *NanoBananaClient, _, _ string) ([]byte, string, error) {
return mustPNGBytes(t), "image/png", nil
}
diff --git a/internal/image/search.go b/internal/image/search.go
index be20731..8de97e0 100644
--- a/internal/image/search.go
+++ b/internal/image/search.go
@@ -28,6 +28,7 @@ type SearchOptions struct {
ImageType string // Type: "photo", "illustration", "vector", "all"
Orientation string // Orientation: "horizontal", "vertical", "all"
CustomPrompt string // Custom prompt for AI image generation
+ AspectRatio string // Override aspect ratio (e.g. "9:16"); empty = provider default
}
// DefaultSearchOptions returns sensible defaults for Bulgarian word searches