1- from google import genai
2- from google .genai .errors import APIError
3- from google .genai .types import GenerateImagesConfig
4- from google .genai .types import PersonGeneration
5- from google .genai .types import SafetyFilterLevel
6- from content import ContentError
7- from content import ImageContent
8- from epd import ensure_rgb
1+ import base64
2+ import binascii
3+ from google import auth
94from io import BytesIO
105from os import environ
116from PIL import Image
7+ import requests
8+
9+ from content import ContentError
10+ from content import ImageContent
11+ from epd import ensure_rgb
1212
1313# The prompt for generating images.
1414IMAGE_PROMPT = """
1515Eastern Christian Orthodox icon with artificial intelligence elements
1616"""
1717
18+ # The model to use for image generation.
19+ IMAGE_MODEL = 'imagen-4.0-ultra-generate-preview-06-06'
20+
21+ # The scope for authenticating with the Google Cloud Vertex AI API.
22+ AUTH_SCOPE = 'https://www.googleapis.com/auth/cloud-platform'
23+
24+ # The location for the Google Cloud Vertex AI API.
25+ LOCATION = 'us-central1'
26+
1827# Supported aspect ratios for the image generation API.
1928ASPECT_RATIOS = [
2029 (1.0 , '1:1' ),
@@ -28,10 +37,23 @@ class AIcon(ImageContent):
2837
2938 def __init__ (self ):
3039 # Configure the API.
31- self ._client = genai .Client (
32- vertexai = True ,
33- project = environ ['GOOGLE_CLOUD_PROJECT' ],
34- location = 'us-central1' )
40+ self ._project = environ ['GOOGLE_CLOUD_PROJECT' ]
41+ self ._location = LOCATION
42+ self ._model = IMAGE_MODEL
43+ self ._credentials , _ = auth .default (scopes = [AUTH_SCOPE ])
44+
45+ def _access_token (self ):
46+ """Gets a fresh access token for API calls."""
47+
48+ # Refresh the credentials, if needed.
49+ if not self ._credentials .valid :
50+ try :
51+ self ._credentials .refresh (auth .transport .requests .Request ())
52+ except auth .exceptions .RefreshError as e :
53+ raise ContentError (f'Failed to refresh credentials: { e } ' )
54+
55+ # Return the access token.
56+ return self ._credentials .token
3557
3658 def image (self , user , width , height , variant ):
3759 """Generates the AI icon image."""
@@ -49,24 +71,41 @@ def calculate_crop_ratio(ratio_tuple):
4971 best_ratio_tuple = min (ASPECT_RATIOS , key = calculate_crop_ratio )
5072 config_aspect_ratio = best_ratio_tuple [1 ]
5173
52- # Call the API to generate the image.
74+ # Request a generated image from the Google Cloud Vertex AI API.
75+ url = (
76+ f'https://{ self ._location } -aiplatform.googleapis.com/v1/'
77+ f'projects/{ self ._project } /locations/{ self ._location } /'
78+ f'publishers/google/models/{ self ._model } :predict' )
79+ headers = {
80+ 'Authorization' : f'Bearer { self ._access_token ()} ' ,
81+ 'Content-Type' : 'application/json' }
82+ payload = {
83+ 'instances' : [{'prompt' : IMAGE_PROMPT }],
84+ 'parameters' : {
85+ 'sampleCount' : 1 ,
86+ 'aspectRatio' : config_aspect_ratio ,
87+ 'personGeneration' : 'allow_all' ,
88+ 'safetyFilterLevel' : 'block_only_high' }}
89+ try :
90+ response = requests .post (
91+ url , headers = headers , json = payload , timeout = 60 )
92+ response .raise_for_status ()
93+ except requests .RequestException as e :
94+ raise ContentError (f'Image generation API request failed: { e } ' )
95+
96+ # Extract the Base64 image data from the response.
5397 try :
54- response = self ._client .models .generate_images (
55- model = 'imagen-4.0-ultra-generate-preview-06-06' ,
56- prompt = IMAGE_PROMPT ,
57- config = GenerateImagesConfig (
58- number_of_images = 1 ,
59- aspect_ratio = config_aspect_ratio ,
60- person_generation = PersonGeneration .ALLOW_ALL ,
61- safety_filter_level = SafetyFilterLevel .BLOCK_ONLY_HIGH ))
62- except APIError as e :
63- raise ContentError (f'Image generation failed: { e } ' )
64- if not response .generated_images :
65- raise ContentError ('Empty image generation response' )
98+ result = response .json ()
99+ prediction = result ['predictions' ][0 ]
100+ image_base64 = prediction ['bytesBase64Encoded' ]
101+ image_bytes = base64 .b64decode (image_base64 )
102+ except (IndexError , KeyError , TypeError ) as e :
103+ raise ContentError (f'Invalid API response: { e } ' )
104+ except (requests .JSONDecodeError , binascii .Error ) as e :
105+ raise ContentError (f'Invalid API response content: { e } ' )
66106
67107 # Scale and crop the generated image.
68- generated_image = response .generated_images [0 ]
69- with BytesIO (generated_image .image .image_bytes ) as image_data :
108+ with BytesIO (image_bytes ) as image_data :
70109 with ensure_rgb (Image .open (image_data )) as image :
71110
72111 # Scale to fill.
0 commit comments