File size: 6,693 Bytes
ad31616
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
'''
DALL-E image generation example for openai>1.2.3, saves requested images as files
-- not a code utility, has no input or return

# example pydantic models returned by client.images.generate(**img_params):
## - when called with "response_format": "url":
images_response = ImagesResponse(created=1699713836, data=[Image(b64_json=None, revised_prompt=None, url='https://oaidalleapiprodscus.blob.core.windows.net/private/org-abcd/user-abcd/img-12345.png?st=2023-11-11T13%3A43%3A56Z&se=2023-11-11T15%3A43%3A56Z&sp=r&sv=2021-08-06&sr=b&rscd=inline&rsct=image/png&skoid=6aaadede-4fb3-4698-a8f6-684d7786b067&sktid=a48cca56-e6da-484e-a814-9c849652bcb3&skt=2023-11-10T21%3A41%3A11Z&ske=2023-11-11T21%3A41%3A11Z&sks=b&skv=2021-08-06&sig=%2BUjl3f6Vdz3u0oRSuERKPzPhFRf7qO8RjwSPGsrQ/d8%3D')])

requires:
pip install --upgrade openai
pip install pillow
'''
import os
from io import BytesIO
import openai                  # for handling error types
from datetime import datetime  # for formatting date returned with images
import base64                  # for decoding images if recieved in the reply
import requests                # for downloading images from URLs
from PIL import Image          # pillow, for processing image types
from prompts import image_style_prompt
from dotenv import load_dotenv
load_dotenv()

api_key = os.getenv("OPENAI_API_KEY")

os.environ["OPENAI_API_KEY"] = api_key

def old_package(version, minimum):  # Block old openai python libraries before today's
    version_parts = list(map(int, version.split(".")))
    minimum_parts = list(map(int, minimum.split(".")))
    return version_parts < minimum_parts

if old_package(openai.__version__, "1.2.3"):
    raise ValueError(f"Error: OpenAI version {openai.__version__}"
                     " is less than the minimum version 1.2.3\n\n"
                     ">>You should run 'pip install --upgrade openai')")

from openai import OpenAI
# client = OpenAI(api_key="sk-xxxxx")  # don't do this, OK?
client = OpenAI()  # will use environment variable "OPENAI_API_KEY"



def generate_DALLE_images(user_prompt, path, filename):

        prompt = (
            f"Subject: {user_prompt} "  # use the space at end
            f"Style: {image_style_prompt}"  # this is implicit line continuation
        )

        image_params = {
            "model": "dall-e-3",  # Defaults to dall-e-2
            "n": 1,  # Between 2 and 10 is only for DALL-E 2
            "size": "1024x1024",  # 256x256, 512x512 only for DALL-E 2 - not much cheaper
            "prompt": prompt,  # DALL-E 3: max 4000 characters, DALL-E 2: max 1000
            "user": "myName",  # pass a customer ID to OpenAI for abuse monitoring
        }

        ## -- You can uncomment the lines below to include these non-default parameters --

        image_params.update({"response_format": "b64_json"})  # defaults to "url" for separate download

        ## -- DALL-E 3 exclusive parameters --
        image_params.update({"model": "dall-e-3"})  # Upgrade the model name to dall-e-3
        image_params.update({"size": "1792x1024"})  # 1792x1024 or 1024x1792 available for DALL-E 3
        # image_params.update({"quality": "hd"})      # quality at 2x the price, defaults to "standard"
        # image_params.update({"style": "natural"})   # defaults to "vivid"

        print(f'Generating image {filename}')
        try:
            images_response = client.images.generate(**image_params)
        except openai.APIConnectionError as e:
            print("Server connection error: {e.__cause__}")  # from httpx.
            raise
        except openai.RateLimitError as e:
            print(f"OpenAI RATE LIMIT error {e.status_code}: (e.response)")
            raise
        except openai.APIStatusError as e:
            print(f"OpenAI STATUS error {e.status_code}: (e.response)")
            raise
        except openai.BadRequestError as e:
            print(f"OpenAI BAD REQUEST error {e.status_code}: (e.response)")
            raise
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
            raise

        # make a file name prefix from date-time of response
        #images_dt = datetime.utcfromtimestamp(images_response.created)
        #img_filename = images_dt.strftime('DALLE-%Y%m%d_%H%M%S')  # like 'DALLE-20231111_144356'
        img_filename = file_path = os.path.join(path, f'{filename}.png')

        # get the prompt used if rewritten by dall-e-3, null if unchanged by AI
        revised_prompt = images_response.data[0].revised_prompt

        # get out all the images in API return, whether url or base64
        # note the use of pydantic "model.data" style reference and its model_dump() method
        image_url_list = []
        image_data_list = []
        for image in images_response.data:
            image_url_list.append(image.model_dump()["url"])
            image_data_list.append(image.model_dump()["b64_json"])

        # Initialize an empty list to store the Image objects
        image_objects = []

        # Check whether lists contain urls that must be downloaded or b64_json images
        if image_url_list and all(image_url_list):
            # Download images from the urls
            for i, url in enumerate(image_url_list):
                while True:
                    try:
                        print(f"getting URL: {url}")
                        response = requests.get(url)
                        response.raise_for_status()  # Raises stored HTTPError, if one occurred.
                    except requests.HTTPError as e:
                        print(f"Failed to download image from {url}. Error: {e.response.status_code}")
                        retry = input("Retry? (y/n): ")  # ask script user if image url is bad
                        if retry.lower() in ["n", "no"]:  # could wait a bit if not ready
                            raise
                        else:
                            continue
                    break
                image_objects.append(Image.open(BytesIO(response.content)))  # Append the Image object to the list
                image_objects[i].save(f"{img_filename}")
                print(f"Saving image to {img_filename}")
        elif image_data_list and all(image_data_list):  # if there is b64 data
            # Convert "b64_json" data to png file
            for i, data in enumerate(image_data_list):
                image_objects.append(Image.open(BytesIO(base64.b64decode(data))))  # Append the Image object to the list
                image_objects[i].save(f"{img_filename}")
                print(f"Saving image to  {img_filename}")
        else:
            print("No image data was obtained. Maybe bad code?")