import gradio as gr import os import openai import time import json import requests import shutil import matplotlib.pyplot as plt from gradio_client import Client from newsapi import NewsApiClient from PIL import Image from gpt_function_definitions import generate_image, generate_music, generate_caption, generate_caption_func, generate_music_func, generate_image_func, dict_plugin_functions #Streaming endpoint API_URL = "https://api.openai.com/v1/chat/completions" # Get the value of the openai_api_key from environment variable openai_api_key = os.getenv("OPENAI_API_KEY") openai.api_key = os.getenv("OPENAI_API_KEY") dicts_list = [value['dict'] for value in dict_plugin_functions.values()] available_function_defns = { key.split('_func')[0]: value['func'] for key, value in dict_plugin_functions.items() } add_plugin_steps = """## Steps to add new Plugins to your Gradio ChatGPT Chatbot Do you want to open this information in a separate tab instead? - Click here. 1. **Acquire the API Endpoint** - You need an API which you can query, and for this example let's consider using a text-to-speech demo hosted on Huggingface Spaces. - **API Endpoint**: [https://gradio-neon-tts-plugin-coqui.hf.space/](https://gradio-neon-tts-plugin-coqui.hf.space/) 2. **Create a Function to Query the API** - You can access any Gradio demo as an API via the Gradio Python Client. ```python from gradio.client import Client def texttospeech(input_text): client = Client("https://gradio-neon-tts-plugin-coqui.hf.space/") result = client.predict( input_text, # str in 'Input' Textbox component "en", # str in 'Language' Radio component api_name="/predict" ) return result ``` 3. **Describe the Function to GPT-3.5** - You need to describe your function to GPT3.5/4. This function definition will get passed to gpt and will suck up your token. GPT may or may not use this function based on user inputs later on. - You can either use the Gradio demo for converting any given function to the required JSON format for GPT-3.5. - Demo: [Function to JSON](https://huggingface.co/spaces/ysharma/function-to-JSON) - Or, you can create the dictionary object on your own. Note that, the correct format is super important here. - MAke sure to name your JSON object description as `_func`. ```python texttospeech_func = { "name": "texttospeech", "description": "generate speech from the given input text", "parameters": { "type": "object", "properties": { "input_text": { "type": "string", "description": "text that will be used to generate speech" } }, "required": [ "input_text" ] } } ``` 4. **Add Function and JSON Object Details** - Add the function definition and description to the `gpt_function_definitions.py` file (simply copy and paste). - `dict_plugin_functions` is a dictionary of all available plugins. Add your plugin information to this dictionary in the required format. ```python 'texttospeech_func': { 'dict': texttospeech_func, 'func': texttospeech } ``` 5. **Update the Chatbot Layout** - Go to the Blocks Chatbot layout and add a new checkbox for your plugin as: ```python texttospeech = gr.Checkbox(label="📝🗣️Text-To-Speech", value=False) ``` - Add the new checkbox component to your submit and click events for your chatbot and to the predict function accordingly. - And also to the `plugins` list in `predict` ```python plugins = [music_gen, stable_diff, image_cap, top_news, texttospeech] ``` Thats it! you are have added your own brand new CHATGPT Plugin for yourself. Go PLAY!! """ # managing conversation with Plugins def run_conversation(user_input, function_call_decision): FLAG_MUSIC, FLAG_IMAGE, FLAG_GEN, FLAG_FUN = False, False, False, False # Step 1: send the conversation and available functions to GPT messages = [{"role": "user", "content": user_input}] functions = dicts_list # example values - [ generate_music_func, generate_image_func] # Attempt to make a request to GPT3.5/4 with retries max_retries = 3 retry_delay = 5 # seconds for attempt in range(max_retries): try: response = openai.ChatCompletion.create( model="gpt-3.5-turbo-0613", messages=messages, functions=functions, function_call=function_call_decision, ) response_message = response["choices"][0]["message"] print(f"response message ^^ -{response_message}") break # If successful, exit the loop except openai.error.ServiceUnavailableError as e: print(f"OpenAI Server is not available. Error: {e}") if attempt < max_retries - 1: print(f"Retrying in {retry_delay} seconds...") time.sleep(retry_delay) else: print("Max retries reached. Exiting.") return None, None, None, False, False, False, False except openai.error.APIError as e: # This will catch API errors from OpenAI print(f"An API error occurred: {e}") if attempt < max_retries - 1: print(f"Retrying in {retry_delay} seconds...") time.sleep(retry_delay) else: print("Max retries reached. Exiting.") return None, None, None, False, False, False, False except Exception as e: # This will catch any other exceptions that are raised. print(f"An unexpected error occurred: {e}") return None, None, None, False, False, False, False # Step 2: check if GPT wanted to call a function if response_message.get("function_call"): FLAG_FUN = True # Step 3: call the function # Note: the JSON response may not always be valid; be sure to handle errors available_functions = available_function_defns # only one function in this example, but you can have multiple function_name = response_message["function_call"]["name"] print(f"function_name - {function_name}") try: function_to_call = available_functions[function_name] function_args = json.loads(response_message["function_call"]["arguments"]) print(f"Logging: fuction_name is - {function_name}") print(f"Logging: fuction_to_call is - {function_to_call}") print(f"Logging: function_args is - {function_args}") function_response = function_to_call(**function_args) print(f"Logging: function_response ^^ is -{function_response}") except KeyError as e: print(f"Function not found: {e}") return response_message, None, None, False, False, False, False except Exception as e: print(f"An error occurred while calling the function: {e}") return response_message, None, None, False, False, False, False if isinstance(function_response, str): if function_response.split('.')[-1] == 'png': FLAG_IMAGE = True elif function_response.split('.')[-1] in ['mp4', "wav", "mp3"]: FLAG_MUSIC = True else: FLAG_GEN = True else: print("PLUGIN FUNCTION RETURNS A NON-STRING OUTPUT: FIX IT TO A STRING OUTPUT TO GET A RESPONSE FROM GPT") # Step 4: send the info on the function call and function response to GPT messages.append(response_message) # extend conversation with assistant's reply messages.append( { "role": "function", "name": function_name, "content": function_response, } ) print(f"Logging: messages is - {messages}") # extend conversation with function response second_response = openai.ChatCompletion.create( model="gpt-3.5-turbo-0613", messages=messages, ) # get a new response from GPT where it can see the function response print(f"Logging: second_response is - {second_response}") print(f"Logging: values of Music, Image, and General flags are respectively - {FLAG_MUSIC}, {FLAG_IMAGE}, {FLAG_GEN}") return response_message, second_response, function_response, FLAG_MUSIC, FLAG_IMAGE, FLAG_GEN, FLAG_FUN else: return response_message, None, None, False, False, False, False #second_response, function_response, FLAG_MUSIC, FLAG_IMAGE, FLAG_GEN, FALG_FUN # driver def predict(inputs, top_p, temperature, chat_counter, music_gen, stable_diff, image_cap, top_news, file_output, plugin_message, chatbot=[], history=[]): #repetition_penalty, top_k #openai.api_key = os.getenv("OPENAI_API_KEY") payload = { "model": "gpt-3.5-turbo-0613", "messages": [{"role": "user", "content": f"{inputs}"}], "temperature" : 1.0, "top_p":1.0, "n" : 1, "stream": True, "presence_penalty":0, "frequency_penalty":0, } headers = { "Content-Type": "application/json", "Authorization": f"Bearer {openai_api_key}" } print(f"chat_counter - {chat_counter}") print(f"music_gen is {music_gen}, stable_diff is {stable_diff}") # file handling print(f"Logging: file_output is - {file_output}") if file_output is not None: files_avail = [f.name for f in file_output ] print(f"Logging: files_available are - {files_avail} ") else: print("Logging: No files available at the moment!") if chat_counter != 0 : messages=[] for data in chatbot: temp1 = {} temp1["role"] = "user" temp1["content"] = data[0] temp2 = {} temp2["role"] = "assistant" temp2["content"] = data[1] messages.append(temp1) messages.append(temp2) temp3 = {} temp3["role"] = "user" temp3["content"] = inputs messages.append(temp3) #messages payload = { "model": "gpt-3.5-turbo", "messages": messages, #[{"role": "user", "content": f"{inputs}"}], "temperature" : temperature, #1.0, "top_p": top_p, #1.0, "n" : 1, "stream": True, "presence_penalty":0, "frequency_penalty":0, } chat_counter+=1 history.append(inputs) print(f"Logging: payload is - {payload}") plugins = [music_gen, stable_diff, image_cap, top_news, ] function_call_decision = "auto" if any(plugins) else "none" #function_call_decision = "none" if not (music_gen or stable_diff) else "auto" #function_call_decision = "auto" if (music_gen or stable_diff or image_cap) else "none" print(f"Logging: function_call_decision flag (auto/none) is - {function_call_decision}") IS_FUN = False first_response = None if function_call_decision == "auto": first_response, second_response, function_response, IS_MUSIC, IS_IMAGE, IS_GEN, IS_FUN = run_conversation(inputs, function_call_decision) print(f"Logging: first_response return value - {first_response}") print(f"Logging: second_response return value - {second_response}") print(f"Logging: function_response return value - {function_response}") print(f"Logging: IS_MUSIC, IS_IMAGE, IS_GEN, IS_FUN, respectively return value - {IS_MUSIC}, {IS_IMAGE}, {IS_GEN}, {IS_FUN}") if (second_response is None) and (first_response is None): bot_response_using_plugins_error = 'Something went wrong! It was either your query or the OpenAI server. I would suggest you can either try again from the start or just reword your last message for more appropriate response.' history.append(bot_response_using_plugins_error) print(f"Logging: history with plugins is - {history}") chat = [(history[i], history[i+1]) for i in range(0, len(history)-1, 2)] + ([(history[-1],)] if len(history) % 2 != 0 else []) print(f"Logging: chat with plugins is - {chat}") yield chat, history, chat_counter, gr.update(visible=True), gr.update(visible=True), gr.update(visible=False) #yield {chatbot: chat, state:history, chat_counter:chat_counter, plugin_message: gr.update(visible=False) } if (second_response is not None): # and (first_response is not None): bot_response_using_plugins = second_response['choices'][0]['message']['content'] print(f"Logging: bot_response_using_plugins using plugins is - {bot_response_using_plugins}") bot_response_using_plugins = bot_response_using_plugins.replace("sandbox:", "") history.append(bot_response_using_plugins) print(f"Logging: history with plugins is - {history}") chat = [(history[i], history[i+1]) for i in range(0, len(history)-1, 2)] + ([(history[-1],)] if len(history) % 2 != 0 else []) print(f"Logging: chat with plugins is - {chat}") if IS_MUSIC: yield chat, history, chat_counter, gr.update(value=function_response), gr.update(visible=True), gr.update(value="⏳ Using MusicGen Plugin") #yield {chatbot: chat, state:history, chat_counter:chat_counter, gen_music:gr.update(value=function_response), plugin_message: gr.update(value="**## ⏳ Using MusicGen Plugin**") } elif IS_IMAGE: yield chat, history, chat_counter, gr.update(visible=True), gr.update(value=function_response), gr.update(value="⏳ Using Diffusers Plugin") #yield {chatbot: chat, state:history, chat_counter:chat_counter, gen_image:gr.update(value=function_response), plugin_message: gr.update(value="**## ⏳ Using Diffusers Plugin**") } elif IS_GEN: yield chat, history, chat_counter, gr.update(visible=True), gr.update(visible=True), gr.update(value="⏳ Using ImageCaption/News Plugin") #yield {chatbot: chat, state:history, chat_counter:chat_counter, plugin_message: gr.update(value="**## ⏳ Using ImageCaption/News Plugin**") } # When no plugins are chosen; or when plugins are chosen but none was used if (function_call_decision == "none") or (first_response is not None and IS_FUN == False): # make a POST request to the API endpoint using the requests.post method, passing in stream=True response = requests.post(API_URL, headers=headers, json=payload, stream=True) #response = requests.post(API_URL, headers=headers, json=payload, stream=True) token_counter = 0 partial_words = "" counter=0 for chunk in response.iter_lines(): #Skipping first chunk if counter == 0: counter+=1 continue #counter+=1 # check whether each line is non-empty if chunk.decode() : chunk = chunk.decode() # decode each line as response data is in bytes if len(chunk) > 12 and "content" in json.loads(chunk[6:])['choices'][0]['delta']: #if len(json.loads(chunk.decode()[6:])['choices'][0]["delta"]) == 0: # break partial_words = partial_words + json.loads(chunk[6:])['choices'][0]["delta"]["content"] if token_counter == 0: history.append(" " + partial_words) else: history[-1] = partial_words chat = [(history[i], history[i + 1]) for i in range(0, len(history) - 1, 2) ] # convert to tuples of list token_counter+=1 yield chat, history, chat_counter, gr.update(visible=True), gr.update(visible=True), gr.update(visible=False) #yield {chatbot: chat, state:history, chat_counter:chat_counter, plugin_message: gr.update(visible=False) } def reset_textbox(): return gr.update(value='') def add_image(file_to_save, file_output): print(f"Logging: image file_to_save is - {file_to_save}") print(f"Logging: files available in directory are -{file_output}") if file_output is not None: file_output = [f.name for f in file_output] if file_to_save is None: return file_output file_output = [file_to_save] if file_output is None else file_output + [file_to_save] print(f"Logging: Updated file directory - {file_output}") return file_output #gr.update(value="dog1.jpg") def add_audio(file_to_save, file_output): print(f"Logging: audio file_to_save is - {file_to_save}") print(f"Logging: files available in directory are -{file_output}") if file_output is not None: file_output = [f.name for f in file_output] if file_to_save is None: return file_output file_output = [file_to_save] if file_output is None else file_output + [file_to_save] print(f"Logging: Updated file directory - {file_output}") return file_output #gr.update(value="dog1.jpg") def upload_file(file, file_output): print(f"Logging: all files available - {file_output}") print(f"Logging: file uploaded is - {file}") img_orig_name = file.name.split('/')[-1] shutil.copy2(file.name, img_orig_name) file_output = [file] if file_output is None else file_output + [file] file_output = [f.name for f in file_output] print(f"Logging: Updated file list is - {file_output}") return file_output messaging = """ How does a Language Model like GPT makes discerning choices regarding which plugins to run? Well, this is done using the Language Model as a reasoning agent and allowing it to assess and process information intelligently:

Function Calling: Interacting with external APIs via free-form text isn't optimal; instead, employing JSON format proves to be a more efficient method.
Gradio Chatbots: Using Gradio and Function Calling you can create chatbots designed to respond to queries by communicating with external APIs. The API responses are fed back to the Language Model for processing and a new response is generated for the user.
Describe your functions to GPT: When integrating with GPT-3.5, specific instructions on how to utilize a particular function or plugin are essential; this encompasses specifying the name, description, and required parameters or inputs. Look at gpt_function_definitions.py for more context.
Caution: Such function definitions would be conveyed to GPT, so when duplicating to build your own Plugins, proceed with caution as functions consume tokens.
Gradio's Usefulness: The versatility of this using Gradio to build LLM applications is immense; In this Gradio app, you can have an array of functions tailored for various purposes, enhancing the breadth and depth of interactions with your Language Model. """ howto = """ Welcome to the ChatGPT-Plugins demo, built using Gradio! This interactive chatbot employs the GPT3.5-turbo-0613 model from OpenAI and boasts custom plugins to enhance your chatting experience. Here’s a quick guide to get you started:

Getting Started: Simply type your messages in the textbox to chat with ChatGPT just like you would in the original app.
Using Plugins: Want to try out a plugin? Check the checkbox next to the plugin you want to use.

DIFFUSERS PLUGIN:
What it does: Generates images based on your text descriptions.
How to use: Type a text description of the image you want to generate, and the plugin will create it for you.
Example input: "Generate an image of a sunset over the mountains."

MUSIC-GEN PLUGIN:
What it does: Generates music based on your descriptions.
How to use: Describe the type of music you want and select an input melody. Remember to upload a melody first!
Example input: "Generate music for a parade using bach.mp3 as input melody."

IMAGE CAPTION PLUGIN:
What it does: Describes images that you upload.
How to use: Upload an image and ask ChatGPT to describe it by name.
Example input: "Describe the image dog.jpg."

NEWS PLUGIN:
What it does: Provides the top 3 news articles based on your search query.
How to use: Simply type in a search query and the plugin will present the top 3 news articles matching your query based on relevance.
Example input: "Show me the top news about space exploration."

Access Generated Content: Find all generated images and audio in the Gradio Files component located below the input textbox.
Have Fun!: Explore and enjoy the versatile features of this Gradio-ChatGPT-PLUGIN demo.
Now you’re all set to make the most of this ChatGPT demo. Happy chatting! """ with gr.Blocks(css = """#col_container { margin-left: auto; margin-right: auto;} #chatbot {height: 520px; overflow: auto;}""") as demo: gr.HTML('

Build Your Own 🧩Plugins For ChatGPT using 🚀Gradio

') with gr.Accordion("Create Plugins for ChatGPT using Gradio in less than 5 minutes!", open=False ): gr.Markdown(add_plugin_steps) with gr.Accordion("How to use the demo and other useful stuff:", open=False): with gr.Accordion("How to use the demo?", open=False): gr.HTML(howto) with gr.Accordion("What is happening?", open=False): gr.HTML(messaging) gr.HTML('''
Duplicate SpaceDuplicate the Space and run securely with your OpenAI API Key
''') #with gr.Column(elem_id = "col_container"): with gr.Row(): with gr.Column(): with gr.Accordion("OpenAI API KEY🔑"): openai_api_key_tb = gr.Textbox(label="Enter your OpenAI API key here", value="🎁GPT3.5 keys are provided by HuggingFace for Free🥳 Don't need to enter yours!😉🙌") plugin_message = gr.Markdown() with gr.Column(): with gr.Accordion("Plug-ins🛠️: Check the box against the plugins you want to use (can select all or few or none)",): music_gen = gr.Checkbox(label="🎵MusicGen", value=False) stable_diff = gr.Checkbox(label="🖼️Diffusers", value=False) image_cap = gr.Checkbox(label="🎨Describe Image", value=False) top_news = gr.Checkbox(label="📰News", value=False) with gr.Row(): with gr.Column(scale=0.7): chatbot = gr.Chatbot(elem_id='chatbot') with gr.Column(scale=0.3): #with gr.Group(): gen_audio = gr.Audio(label="generated audio") gen_image = gr.Image(label="generated image", type="filepath") with gr.Row(): with gr.Column(scale=0.85): inputs = gr.Textbox(placeholder= "Hi there!", label= "Type an input and press Enter") with gr.Column(scale=0.15, min_width=0): btn = gr.UploadButton("📁Upload", file_types=["image", "audio"], file_count="single") state = gr.State([]) #s b1 = gr.Button("🏃Run") with gr.Row(): with gr.Accordion("Parameters", open=False): top_p = gr.Slider( minimum=-0, maximum=1.0, value=1.0, step=0.05, interactive=True, label="Top-p (nucleus sampling)",) temperature = gr.Slider( minimum=-0, maximum=5.0, value=1.0, step=0.1, interactive=True, label="Temperature",) chat_counter = gr.Number(value=0, visible=False, precision=0) with gr.Accordion("Files", open=False): file_output = gr.File(file_count="multiple", file_types=["image", "audio"]) inputs.submit( predict, [inputs, top_p, temperature, chat_counter, music_gen, stable_diff, image_cap, top_news, file_output, plugin_message, chatbot, state], [chatbot, state, chat_counter, gen_audio, gen_image, plugin_message],) b1.click( predict, [inputs, top_p, temperature, chat_counter, music_gen, stable_diff, image_cap, top_news, file_output, plugin_message, chatbot, state], [chatbot, state, chat_counter, gen_audio, gen_image, plugin_message],) b1.click(reset_textbox, [], [inputs]) inputs.submit(reset_textbox, [], [inputs]) btn.upload(upload_file, [btn, file_output], file_output) gen_image.change(add_image, [gen_image, file_output], file_output) gen_audio.change(add_audio, [gen_audio, file_output], file_output) gr.HTML("""Bonus! Follow these steps for adding your own Plugins to this chatbot: How to add new Plugins in ChatGPT in 5 mins!! or open the accordion given on top.""") demo.queue(concurrency_count=2, max_size=10).launch(debug=True, height = '1000')