Skip to content

Commit 47bb1c7

Browse files
committed
Remove Google GenAI Python SDK dependency
Better memory performance with the plain REST API.
1 parent 5e47acd commit 47bb1c7

2 files changed

Lines changed: 66 additions & 28 deletions

File tree

server/aicon.py

Lines changed: 66 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,29 @@
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
94
from io import BytesIO
105
from os import environ
116
from 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.
1414
IMAGE_PROMPT = """
1515
Eastern 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.
1928
ASPECT_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.

server/requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ firebase-admin==6.5.0
55
Flask==3.0.3
66
google-api-python-client==2.129.0
77
google-cloud-vision==3.7.2
8-
google-genai==1.27.0
98
numpy==1.26.4
109
oauth2client==4.1.3
1110
Pillow==10.3.0

0 commit comments

Comments
 (0)