Introduction to the Image Generator Service

Welcome to the third lesson of our course on building an image generation service with FastAPI! In our previous lessons, we created the PromptManager to format user inputs into detailed prompts and the ImageManager to handle storing and processing generated images. Now, we're ready to build the core component that brings everything together: the ImageGeneratorService.

The ImageGeneratorService is the central piece of our application that will:

  1. Connect to Google's Gemini API to generate images via the gemini-3.1-flash-image model
  2. Use our PromptManager to format user inputs into effective prompts
  3. Extract the image data from the Gemini response and store them using our ImageManager
  4. Provide access to all previously generated images

This service acts as the bridge between our application's components and the external AI service that actually creates the images. By encapsulating all the image generation logic in a dedicated service class, we maintain a clean separation of concerns in our application architecture.

In this lesson, we'll implement this service step by step, from setting up the API client to handling responses and errors. By the end, you'll have a fully functional image generation service that you can later integrate into a FastAPI web application.

Setting Up the Gemini API Client

Before we can generate images, we need to set up a client to communicate with Google's Gemini API. The Gemini API provides access to Google's powerful native image generation models, allowing us to create high-quality images from text prompts.

First, we need to install the Google Generative AI library. In a typical development environment, you would run:

pip install google-genai

Remember that in the CodeSignal environment, this library is already installed, so you won't need to run this command there.

Now, let's create our ImageGeneratorService class and set up the client in the constructor. We'll create a new file called image_generator_service.py in the app/services directory:

import os
from google import genai
from google.genai import types
from models.image_manager import ImageManager
from models.prompt_manager import PromptManager

GEMINI_IMAGE_MODEL = "gemini-3.1-flash-image"

class ImageGeneratorService:
    def __init__(self):
        self.image_manager = ImageManager()

        api_key = os.getenv("GEMINI_API_KEY")
        if not api_key:
            raise ValueError("GEMINI_API_KEY not found in environment variables.")

        base_url = os.getenv("GEMINI_BASE_URL")
        if not base_url:
            raise ValueError("GEMINI_BASE_URL not found in environment variables.")

        self.gemini_client = genai.Client(
            api_key=api_key,
            http_options=types.HttpOptions(
                base_url=base_url,
            ),
        )

In this constructor, we're doing two important things:

  1. Creating an instance of our ImageManager class to handle storing and retrieving images
  2. Safely retrieving our environment variables and initializing the Gemini client.

The genai.Client is the main interface for interacting with Google's Generative AI services. We'll use this client to access the native Gemini image generation model.

Implementing the Image Generation Logic

Now that we have our client set up, let's implement the core method of our service: generate_image(). This method will take a user input string, format it into a detailed prompt using our PromptManager, send the request to the Gemini API, and store the resulting image using our ImageManager.

Here's the implementation:

def generate_image(self, user_input: str) -> str:
    prompt = PromptManager.format_prompt(user_input)
    try:
        response = self.gemini_client.models.generate_content(
            model=GEMINI_IMAGE_MODEL,
            contents=prompt,
            config=types.GenerateContentConfig(
                response_modalities=["IMAGE"],
                image_config=types.ImageConfig(
                    aspect_ratio="16:9",
                )
            ),
        )

        image_parts = [part for part in response.parts if part.inline_data]

        if not image_parts:
            raise RuntimeError("No image returned from Gemini")

        image = image_parts[0].as_image()
        return self.image_manager.add_image(prompt, image)
    except Exception as e:
        raise RuntimeError(f"Error generating image: {str(e)}")

Let's break down what's happening in this method:

  1. We call PromptManager.format_prompt() to convert the user's input into a detailed prompt using our predefined template.
  2. We make the API call using self.gemini_client.models.generate_content(), specifying:
    • model: The gemini-3.1-flash-image model.
    • contents: The formatted prompt.
    • config: A configuration object specifying we expect an IMAGE modality and want a 16:9 aspect ratio.
  3. We extract the image data from the response parts and convert the first available image part into a native image object using .as_image().
  4. Finally, we pass the prompt and image object to our ImageManager's add_image() method, which converts the image to base64 format, stores it, and returns the base64 string.
Error Handling and Service Integration

Generating images through an external API can fail for various reasons. To make our service robust, we've wrapped the API call in a try-except block that catches any exceptions and raises a more informative RuntimeError.

Now, let's add one more method to our service to retrieve all previously generated images:

def get_all_images(self):
    return self.image_manager.get_images()

This method delegates to our ImageManager's get_images() method, returning the complete list of stored images along with their associated prompts.

Testing the Complete Service

Now that we've implemented our ImageGeneratorService, let's create a test script to verify that it works correctly. We'll update our app/main.py file to use the new service:

from services.image_generator_service import ImageGeneratorService
from models.prompt_manager import PromptManager
import os

user_input = "Luxury Tech Conference 2025: Innovating the Future - April 10th, New York City"

# Initialize ImageGeneratorService
try:
    image_service = ImageGeneratorService()
    service_result = image_service.generate_image(user_input)
    print("Image Generated Successfully:")
    print(service_result)
except Exception as e:
    print(f"Error generating image: {str(e)}")

# Retrieve all images
print("\nAll Stored Images:")
print(image_service.get_all_images())

When running this script with a valid API key, you would see the generated base64 string representing the encoded image data.

Summary and Practice Preview

In this lesson, we've built the ImageGeneratorService, the core component of our image generation application. This service connects our previously built components (PromptManager and ImageManager) to Google's Gemini API, allowing us to generate high-quality images from text prompts using Gemini 3.1 Flash.

Let's review what we've learned:

  1. We set up a client to communicate with Google's Gemini API.
  2. We implemented the generate_image() method using generate_content(...) to create images from user inputs.
  3. We added robust error handling to deal with potential API issues.
  4. We created a method to retrieve all previously generated images.

With the ImageGeneratorService in place, we're one step closer to having a complete image generation web application powered by FastAPI!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal