import os from dotenv import load_dotenv, find_dotenv import gradio as gr import openai import requests from PIL import Image from io import BytesIO # load the secrets if running locally _ = load_dotenv(find_dotenv(filename="secrets.env", raise_error_if_not_found=False)) # Global variable AUTH_USERNAME = os.environ["AUTH_USERNAME"] AUTH_PASSWORD = os.environ["AUTH_PASSWORD"] # Load credentials openai.api_key = os.environ["OPENAI_API_KEY"] SYSTEM_PROMPT = "You are a helpful assistant and do your best to answer the user's questions.\ You do not make up answers." # define the function that will make the API calls for the catbot def chatBotCompletionApiCall(prompt:str, temperature = 0.7, max_tokens = 1024, model="GPT-3.5", stream=True): if model == "GPT-3.5": model = "gpt-3.5-turbo-0125" else: model = "gpt-4-turbo-preview" # make the API call with the given parameter response = openai.chat.completions.create( model=model, messages=prompt, max_tokens = max_tokens, temperature=temperature, stream=stream, ) # return the completed text if stream: for chunk in response: output = chunk.choices[0].delta.content # when Stream is set to True yield output else: output = response.choices[0].message.content # when Stream is set to False # Helper function: format the prompt to include history for fhe chatbot def chatBotFormatPrompt(newMsg:str, chatHistory, instruction): # start with the system prompt messages = [] messages.append({ "role": "system", "content": instruction }) # add the history for turn in chatHistory: # retrieve the user and assistant messages from history userMsg, AssistantMsg = turn # add the user message messages.append({ "role": "user", "content": userMsg }) # add the assistant message messages.append({ "role": "assistant", "content": AssistantMsg }) # add the last message that needs to be answer messages.append({ "role": "user", "content": newMsg }) # return the formated messages return messages # def the response function (to get the answer as one block after generation) def responseChatBot(newMsg:str, chatHistory, instruction, temperature, max_tokens, model, stream=False): prompt = chatBotFormatPrompt(newMsg=newMsg, chatHistory=chatHistory, instruction=instruction) response = chatBotCompletionApiCall(prompt=prompt, temperature=temperature, max_tokens=max_tokens, model=model) chatHistory.append([newMsg, response]) return "", chatHistory # def the streamResponse function, to stream the results as they are generated def streamResponseChatBot(newMsg:str, chatHistory, instruction, temperature, max_tokens, model, stream = True): chatHistory.append([newMsg, ""]) prompt = chatBotFormatPrompt(newMsg=newMsg, chatHistory=chatHistory, instruction=instruction) stream = chatBotCompletionApiCall(prompt=prompt, temperature=temperature, max_tokens=max_tokens, model=model) for chunk in stream: if chunk != None: chatHistory[-1][1] += chunk yield "", chatHistory else: return "", chatHistory # helper function for image generation def generateImageOpenAI(prompt, size = "1024x1024", quality = "standard", model = "dall-e-3", n=1): ''' Make an API call to OpenAI's DALL-E model and return the generated image in PIL format ''' print("request sent") openAIresponse = openai.images.generate(model=model, prompt=prompt,size=size,quality=quality,n=n,) image_url = openAIresponse.data[0].url # get the image in Bytes format imageResponse = requests.get(url=image_url) imageBytes = imageResponse.content # convert it to PIL format image = Image.open(BytesIO(imageBytes)) print("image received!") # return the result return image # Define some components model = gr.Dropdown( choices=["GPT-3.5", "GPT-4"], value="GPT-3.5", multiselect=False, label="Model", info="Choose the model you want to chat with.\nGo easy on GPT-4: it costs 500 times more than GPT 3.5!" ) instruction = gr.Textbox( value=SYSTEM_PROMPT, label="System instructions", lines=4,) temperature = gr.Slider( minimum=0, maximum=2, step=0.1, value=0.7, label="Temperature", info="The higher, the more random the results will be" ) max_token = gr.Slider( minimum=64, maximum=2048, step=64, value=1024, label="Max Token", info="Maximum number of token the model will take into consideration" ) # Components for Image generator genImage = gr.Image( label="Result", type="pil", render = False ) # Box for generated image # def helper function to update and render the component def generateAndRender(prompt:str, size, quality,): ''' Send the request to the API endpoint and update the components. Outputs: - oldPrompt - genImage - promptBox ''' # get the image image = generateImageOpenAI(prompt, size, quality) # update the components oldPrompt = gr.Textbox(value=prompt, label = "Your prompt", render=True) genImage = gr.Image(value=image, label="Result", type="pil", render = True) promptBox = gr.Textbox(label="Enter your prompt", lines=3) # return the components return oldPrompt, genImage, promptBox # Build the app with gr.Blocks(theme='Insuz/Mocha', css="style.css") as app: # First tab: chatbot with gr.Tab(label="ChatBot"): with gr.Row(): with gr.Column(scale = 8, elem_classes=["float-left"]): gr.Markdown("# Private GPT") gr.Markdown("This chatbot is powered by the openAI GPT series.\ The default model is `GPT-3.5`, but `GPT-4` can be selected in the advanced options.\ \nAs it uses the openAI API, user data is not used to train openAI models (see their official [website](https://help.openai.com/en/articles/5722486-how-your-data-is-used-to-improve-model-performance)).") chatbot = gr.Chatbot() # Associated variable: chatHistory msg = gr.Textbox(label="Message") with gr.Row(): with gr.Column(scale=4): Button = gr.Button(value="Submit") with gr.Column(scale=4): clearButton = gr.ClearButton([chatbot, msg]) msg.submit( fn=streamResponseChatBot, inputs=[msg, chatbot, instruction, temperature, max_token, model], outputs=[msg, chatbot] ) Button.click( fn=streamResponseChatBot, inputs=[msg, chatbot, instruction, temperature, max_token, model], outputs=[msg, chatbot] ) with gr.Column(scale = 1, elem_classes=["float-right"]): with gr.Accordion(label="Advanced options", open=True): model.render() instruction.render() temperature.render() max_token.render() # Second Tab: image generation with gr.Tab(label="Image Creation"): # Title and description gr.Markdown("# Image generation") gr.Markdown("Powered by OpenAI's `DALL-E 3` Model under the hood.\n\ You can change the `size` as well as the `quality`.") # First row: prompt with gr.Row(): prompt = gr.Textbox(label="Enter your prompt", lines=3) # Second row: allow for advanced customization with gr.Accordion(label="Advanced option", open=False): # should not be visible by default # Three columns of advanced options with gr.Row(): with gr.Column(): size = gr.Dropdown( choices = ["1024x1024", "1024x1792","1792x1024"], value = "1024x1024", info = "Choose the size of the image", ) with gr.Column(): quality = gr.Dropdown( choices = ["standard", "hd"], value = "standard", info="Define the quality of the image", ) model = gr.Text(value="dall-e-3", render=False) n = gr.Text(value=1, render=False) # Button # Submit and clear with gr.Row(): with gr.Column(): button = gr.Button(value="submit", min_width=30, ) with gr.Column(): clearImageButton = gr.ClearButton(components=[prompt, genImage]) # Generated Image genImage.render() # Not rendered - logic of the app button.click( fn=generateImageOpenAI, inputs=[prompt, size, quality], outputs=[genImage], ) prompt.submit( fn=generateImageOpenAI, inputs=[prompt, size, quality], outputs=[genImage], ) gr.close_all() app.queue().launch(auth=(AUTH_USERNAME, AUTH_PASSWORD)) # app.queue().launch(share=False)