# Image Generation

The Go AI SDK provides the [`ai.GenerateImage()`](https://goaisdk.com/docs/reference/ai/generate-image.md) function to generate images based on a given prompt using an image model.

```go
package main

import (
    "context"
    "fmt"
    "log"
    "os"

    "github.com/digitallysavvy/go-ai/pkg/ai"
    "github.com/digitallysavvy/go-ai/pkg/providers/openai"
)

func main() {
    ctx := context.Background()

    provider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")})
    imageModel, _ := provider.ImageModel("dall-e-3")

    result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
        Model:  imageModel,
        Prompt: "Santa Claus driving a Cadillac",
    })
    if err != nil {
        log.Fatal(err)
    }

    // Access image data
    fmt.Printf("Image MIME type: %s\n", result.Image.MediaType)
    fmt.Printf("Image size: %d bytes\n", len(result.Image.Data))

    // Save image to file
    err = os.WriteFile("generated.png", result.Image.Data, 0644)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Image saved to generated.png")
}
```

You can access the image data in different formats:

```go
// As bytes
imageBytes := result.Image.Data

// As base64 string
base64String := result.Image.Base64()

// Get image MIME type
mimeType := result.Image.MediaType
```

## Settings

### Size and Aspect Ratio

Depending on the model, you can specify either the size or the aspect ratio.

#### Size

The size is specified as a string in the format `{width}x{height}`. Models only support specific sizes, and the supported sizes vary by model and provider.

```go
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    Size:   "1024x1024",
})
```

#### Aspect Ratio

The aspect ratio is specified as a string in the format `{width}:{height}`. Models only support specific aspect ratios, which vary by model and provider.

```go
provider := google.New(google.Config{APIKey: os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY")})
imageModel, _ := provider.ImageModel("imagen-4.0-generate-001")

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    Size:   "1920x1080", // Automatically converts to 16:9 aspect ratio
})
```

### Generating Multiple Images

`GenerateImage` supports generating multiple images at once:

```go
n := 4 // Number of images to generate

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    N:      &n,
})
if err != nil {
    log.Fatal(err)
}

// Access all images
for i, image := range result.Images {
    filename := fmt.Sprintf("image-%d.png", i+1)
    err := os.WriteFile(filename, image.Data, 0644)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("Saved %s\n", filename)
}
```

> **Note:** `GenerateImage` will automatically batch requests as needed to generate the requested number of images.

Each image model has an internal limit on how many images it can generate in a single API call. The SDK manages this automatically by batching requests appropriately. By default, the SDK uses provider-documented limits (e.g., DALL-E 3 can only generate 1 image per call, while DALL-E 2 supports up to 10).

You can override this behavior using `MaxImagesPerCall`:

```go
n := 10 // Will make 2 calls of 5 images each

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:            imageModel,
    Prompt:           "Santa Claus driving a Cadillac",
    MaxImagesPerCall: 5, // Override default batch size
    N:                &n,
})
```

### Providing a Seed

You can provide a seed to control the output of the image generation process. If supported by the model, the same seed will always produce the same image.

```go
seed := 1234567890

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    Seed:   &seed,
})
```

### Provider-Specific Settings

Image models often have provider- or model-specific settings. You can pass such settings using the `ProviderOptions` parameter:

```go
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    Size:   "1024x1024",
    ProviderOptions: map[string]interface{}{
        "openai": map[string]interface{}{
            "style":   "vivid",
            "quality": "hd",
        },
    },
})
```

### Timeouts with Context

Use Go's context for timeouts and cancellation:

```go
// Timeout after 60 seconds
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
})
if err != nil {
    if ctx.Err() == context.DeadlineExceeded {
        fmt.Println("Image generation timed out")
    }
    log.Fatal(err)
}
```

### Custom Headers

Add custom headers to the image generation request:

```go
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
    Headers: map[string]string{
        "X-Custom-Header": "custom-value",
    },
})
```

### Warnings

If the model returns warnings, they will be available in the `Warnings` field:

```go
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
})
if err != nil {
    log.Fatal(err)
}

for _, warning := range result.Warnings {
    fmt.Printf("Warning: %s\n", warning.Message)
}
```

### Additional Provider Metadata

Some providers expose additional metadata for the result:

```go
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Santa Claus driving a Cadillac",
})
if err != nil {
    log.Fatal(err)
}

// Access provider-specific metadata
if metadata, ok := result.ProviderMetadata["openai"].(map[string]interface{}); ok {
    if images, ok := metadata["images"].([]interface{}); ok && len(images) > 0 {
        if imageMetadata, ok := images[0].(map[string]interface{}); ok {
            if revisedPrompt, ok := imageMetadata["revisedPrompt"].(string); ok {
                fmt.Printf("Original prompt: %s\n", "Santa Claus driving a Cadillac")
                fmt.Printf("Revised prompt: %s\n", revisedPrompt)
            }
        }
    }
}
```

## Error Handling

Handle image generation errors:

```go
import providererrors "github.com/digitallysavvy/go-ai/pkg/provider/errors"

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Generate an image",
})
if err != nil {
    // Check for specific error types
    var noImageErr *ai.NoImageGeneratedError
    var rateLimitErr *providererrors.RateLimitError
    if errors.As(err, &noImageErr) {
        fmt.Println("No image was generated")
    } else if errors.As(err, &rateLimitErr) {
        fmt.Println("Rate limit exceeded")
    } else {
        fmt.Printf("Error: %v\n", err)
    }
    return
}
```

## Practical Examples

### Saving Images in Different Formats

```go
func saveImage(image []byte, mimeType string, filename string) error {
    ext := "png"
    if mimeType == "image/jpeg" {
        ext = "jpg"
    }

    return os.WriteFile(fmt.Sprintf("%s.%s", filename, ext), image, 0644)
}

result, _ := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imageModel,
    Prompt: "Beautiful sunset",
})

saveImage(result.Image.Data, result.Image.MediaType, "sunset")
```

### Generating Image Variations

Generate multiple variations of the same prompt:

```go
func generateVariations(ctx context.Context, model provider.ImageModel, prompt string, count int) ([]types.GeneratedFile, error) {
    result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
        Model:  model,
        Prompt: prompt,
        N:      &count,
    })
    if err != nil {
        return nil, err
    }

    return result.Images, nil
}

images, _ := generateVariations(ctx, imageModel, "Abstract art with vibrant colors", 4)

for i, img := range images {
    os.WriteFile(fmt.Sprintf("variation-%d.png", i+1), img.Data, 0644)
}
```

### Batch Image Generation

Generate multiple different images:

```go
prompts := []string{
    "A serene mountain landscape",
    "A bustling city street at night",
    "An underwater coral reef",
    "A cozy coffee shop interior",
}

for i, prompt := range prompts {
    result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
        Model:  imageModel,
        Prompt: prompt,
    })
    if err != nil {
        log.Printf("Error generating image %d: %v\n", i+1, err)
        continue
    }

    filename := fmt.Sprintf("batch-%d.png", i+1)
    os.WriteFile(filename, result.Image.Data, 0644)
    fmt.Printf("Generated %s: %s\n", filename, prompt)
}
```

### Image Generation with Retry Logic

```go
func generateImageWithRetry(ctx context.Context, model provider.ImageModel, prompt string, maxRetries int) (*ai.GenerateImageResult, error) {
    var lastErr error

    for attempt := 0; attempt <= maxRetries; attempt++ {
        if attempt > 0 {
            fmt.Printf("Retry attempt %d/%d\n", attempt, maxRetries)
            time.Sleep(time.Duration(attempt) * time.Second)
        }

        result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
            Model:  model,
            Prompt: prompt,
        })

        if err == nil {
            return result, nil
        }

        lastErr = err

        // Don't retry on certain errors
        if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
            break
        }
    }

    return nil, fmt.Errorf("failed after %d attempts: %w", maxRetries+1, lastErr)
}
```

### Google Image Generation Examples

#### Google Generative AI - Imagen 4.0

```go
// High-quality image generation with Imagen 4.0
googleProvider := google.New(google.Config{
    APIKey: os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY"),
})

imagenModel, _ := googleProvider.ImageModel("imagen-4.0-generate-001")

n := 1
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imagenModel,
    Prompt: "A serene mountain landscape at sunset with dramatic clouds",
    N:      &n,
    Size:   "1024x1024", // Square 1:1 aspect ratio
})
if err != nil {
    log.Fatal(err)
}

os.WriteFile("imagen_4_output.png", result.Image.Data, 0644)
fmt.Printf("Generated with Imagen 4.0: %d bytes\n", len(result.Image.Data))
```

#### Google Generative AI - Gemini Flash Image

```go
// Fast image generation with Gemini
geminiModel, _ := googleProvider.ImageModel("gemini-2.5-flash-image")

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  geminiModel,
    Prompt: "Abstract colorful art with geometric shapes and vibrant patterns",
    Size:   "1920x1080", // Widescreen 16:9
})
if err != nil {
    log.Fatal(err)
}

os.WriteFile("gemini_output.png", result.Image.Data, 0644)
```

#### Google Vertex AI - Imagen 3.0

```go
// Enterprise-grade image generation with Vertex AI
vertexProvider, err := googlevertex.New(googlevertex.Config{
    Project:     os.Getenv("GOOGLE_VERTEX_PROJECT"),
    Location:    os.Getenv("GOOGLE_VERTEX_LOCATION"),
    AccessToken: os.Getenv("GOOGLE_VERTEX_ACCESS_TOKEN"),
})
if err != nil {
    log.Fatal(err)
}

vertexModel, _ := vertexProvider.ImageModel("imagen-3.0-generate-001")

n := 1
result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  vertexModel,
    Prompt: "A futuristic cityscape at night with neon lights",
    N:      &n,
    Size:   "1920x1080", // 16:9 aspect ratio
})
if err != nil {
    log.Fatal(err)
}

os.WriteFile("vertex_imagen_output.png", result.Image.Data, 0644)
```

#### Comparing Google Models

```go
prompts := []string{
    "A magical forest with glowing mushrooms",
    "Cyberpunk city street at night",
    "Mountain landscape with aurora borealis",
}

models := map[string]provider.ImageModel{
    "imagen-4.0":    imagenModel,
    "gemini-flash":  geminiModel,
    "vertex-imagen": vertexModel,
}

for name, model := range models {
    for i, prompt := range prompts {
        result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
            Model:  model,
            Prompt: prompt,
            Size:   "1024x1024",
        })
        if err != nil {
            log.Printf("Error with %s: %v\n", name, err)
            continue
        }

        filename := fmt.Sprintf("%s_%d.png", name, i+1)
        os.WriteFile(filename, result.Image.Data, 0644)
        fmt.Printf("✓ Generated %s\n", filename)
    }
}
```

## Image Models

Several providers offer image generation models:

| Provider | Model | Supported Sizes/Aspect Ratios |
|----------|-------|------------------------------|
| **xAI** | `grok-imagine-image` | Flexible aspect ratios (1:1, 16:9, 9:16, 4:3, etc.) |
| **OpenAI** | `gpt-image-1` | 1024x1024, 1536x1024, 1024x1536 |
| **OpenAI** | `dall-e-3` | 1024x1024, 1792x1024, 1024x1792 |
| **OpenAI** | `dall-e-2` | 256x256, 512x512, 1024x1024 |
| **Amazon Bedrock** | `amazon.nova-canvas-v1:0` | 320-4096 (multiples of 16) |
| **Fal** | `fal-ai/flux/dev` | 1:1, 3:4, 4:3, 9:16, 16:9, 9:21, 21:9 |
| **Fal** | `fal-ai/flux-pro/v1.1-ultra` | 1:1, 3:4, 4:3, 9:16, 16:9, 9:21, 21:9 |
| **Google** | `imagen-4.0-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
| **Google** | `gemini-2.5-flash-image` | 1:1, 3:4, 4:3, 9:16, 16:9 |
| **Google Vertex** | `imagen-3.0-generate-001` | 1:1, 3:4, 4:3, 9:16, 16:9 |
| **Black Forest Labs** | `flux-pro-1.1-ultra` | 3:7 to 7:3 aspect ratios |
| **Stability AI** | Various via providers | Multiple sizes supported |

### Example with Different Providers

```go
// XAI (Grok)
xaiProvider := xai.New(xai.Config{APIKey: os.Getenv("XAI_API_KEY")})
grokModel, _ := xaiProvider.ImageModel("grok-imagine-image")

// OpenAI
openaiProvider := openai.New(openai.Config{APIKey: os.Getenv("OPENAI_API_KEY")})
dalleModel, _ := openaiProvider.ImageModel("dall-e-3")

// Google Generative AI - Imagen 4.0
googleProvider := google.New(google.Config{APIKey: os.Getenv("GOOGLE_GENERATIVE_AI_API_KEY")})
imagenModel, _ := googleProvider.ImageModel("imagen-4.0-generate-001")

// Google Generative AI - Gemini Image
geminiImageModel, _ := googleProvider.ImageModel("gemini-2.5-flash-image")

// Google Vertex AI - Imagen 3.0
vertexProvider, _ := googlevertex.New(googlevertex.Config{
    Project:     os.Getenv("GOOGLE_VERTEX_PROJECT"),
    Location:    os.Getenv("GOOGLE_VERTEX_LOCATION"),
    AccessToken: os.Getenv("GOOGLE_VERTEX_ACCESS_TOKEN"),
})
vertexImagenModel, _ := vertexProvider.ImageModel("imagen-3.0-generate-001")

// Fal
falProvider := fal.New(fal.Config{APIKey: os.Getenv("FAL_API_KEY")})
fluxModel, _ := falProvider.ImageModel("fal-ai/flux/dev")

// Use any model with the same API
result, _ := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  imagenModel, // or grokModel, dalleModel, geminiImageModel, vertexImagenModel, or fluxModel
    Prompt: "A beautiful landscape",
})
```

## Advanced Features

### Image Editing

Some providers support editing existing images with natural language instructions:

```go
// XAI image editing
imageData, _ := os.ReadFile("source.jpg")

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  grokModel,
    Prompt: "Change the sky to vibrant sunset colors",
    Files: []provider.ImageFile{
        {
            Type:      "file",
            Data:      imageData,
            MediaType: "image/jpeg",
        },
    },
})
```

### Checking File and Mask Support

Not every model that accepts `Files` / `Mask` actually consumes them for editing; some silently ignore them and return an unrelated fresh generation. A model can optionally advertise support through `SupportsFileInputs() *bool` / `SupportsMaskInputs() *bool`, read via `provider.ImageModelSupportsFileInputs` / `provider.ImageModelSupportsMaskInputs`. `nil` means unknown — only route an editing request to a model when the result is a non-nil `true`:

```go
if supported := provider.ImageModelSupportsFileInputs(model); supported != nil && *supported {
    // safe to pass Files for image editing
}
```

### Image Inpainting

Fill specific areas of an image with new content using masks:

```go
sourceImage, _ := os.ReadFile("source.jpg")
maskImage, _ := os.ReadFile("mask.png")

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  grokModel,
    Prompt: "Add a rainbow in the masked area",
    Files: []provider.ImageFile{
        {Type: "file", Data: sourceImage, MediaType: "image/jpeg"},
    },
    Mask: &provider.ImageFile{
        Type:      "file",
        Data:      maskImage,
        MediaType: "image/png",
    },
})
```

**Mask Requirements:**
- White pixels (255,255,255) indicate areas to modify
- Black pixels (0,0,0) indicate areas to preserve
- Mask should match source image dimensions

### Image Variations

Generate multiple variations of a source image:

```go
sourceImage, _ := os.ReadFile("source.jpg")
n := 4

result, err := ai.GenerateImage(ctx, ai.GenerateImageOptions{
    Model:  grokModel,
    Prompt: "Generate creative variations with different styles",
    N:      &n,
    Files: []provider.ImageFile{
        {Type: "file", Data: sourceImage, MediaType: "image/jpeg"},
    },
})
```

Providers that can return multiple images expose every image through `result.Images`. Low-level provider results also preserve provider-returned base64 strings in `result.Base64Images`; the high-level `ai.GenerateImage` helper decodes those strings into `GeneratedFile.Data` so JSON output contains the expected base64 image data.

## Best Practices

1. **Set Timeouts**: Image generation can be slow; always use context with timeout
2. **Handle Errors**: Implement proper error handling and retry logic
3. **Validate Prompts**: Ensure prompts are appropriate and within provider guidelines
4. **Choose Right Model**: Different models excel at different styles and use cases
5. **Use Appropriate Sizes**: Request sizes supported by the model
6. **Save Efficiently**: Use appropriate image formats and compression
7. **Monitor Costs**: Track image generation usage and costs
8. **Respect Rate Limits**: Implement rate limiting for bulk generation
9. **Cache Results**: Cache generated images when appropriate
10. **Test Prompts**: Iterate on prompts to get desired results

## Next Steps

- Learn about [Speech Generation](https://goaisdk.com/docs/ai-sdk-core/speech.md)
- Explore [Provider Documentation](https://goaisdk.com/docs/providers/overview.md)
- See [Examples](https://github.com/digitallysavvy/go-ai/tree/main/examples)

## See Also

- [Providers](https://goaisdk.com/docs/providers/overview.md)
- [Settings](https://goaisdk.com/docs/ai-sdk-core/settings.md)
- [Error Handling](https://goaisdk.com/docs/ai-sdk-core/error-handling.md)
- [API Reference: GenerateImage](https://goaisdk.com/docs/reference/ai/generate-image.md)
