Spaces:
Runtime error
Runtime error
| import time | |
| import os | |
| import openai | |
| import json | |
| from threading import Thread | |
| from queue import Queue, Empty | |
| from threading import Thread | |
| from collections.abc import Generator | |
| from typing import Any, Dict, List, Union | |
| from langchain.vectorstores import FAISS | |
| from langchain.chains import ConversationChain, LLMChain, SequentialChain | |
| from langchain.chat_models import ChatOpenAI | |
| from langchain.prompts import ChatPromptTemplate, PromptTemplate | |
| from langchain.llms import OpenAI | |
| from langchain.callbacks.base import BaseCallbackHandler | |
| from langchain.callbacks import PromptLayerCallbackHandler | |
| from langchain.prompts.chat import ( | |
| ChatPromptTemplate, | |
| SystemMessagePromptTemplate, | |
| AIMessagePromptTemplate, | |
| HumanMessagePromptTemplate, | |
| ) | |
| from langchain.memory import ConversationSummaryMemory | |
| import promptlayer | |
| from pathlib import Path | |
| from transformers import pipeline | |
| import requests | |
| import torch | |
| from diffusers import StableDiffusionPipeline | |
| from threading import Thread | |
| from queue import Queue, Empty | |
| from threading import Thread | |
| from collections.abc import Generator | |
| from langchain.llms import OpenAI | |
| from langchain.callbacks.base import BaseCallbackHandler | |
| import gradio as gr | |
| #Load the FAISS Model ( vector ) | |
| openai.api_key = os.environ["OPENAI_API_KEY"] | |
| #API Keys | |
| promptlayer.api_key = os.environ["PROMPTLAYER"] | |
| # Defined a QueueCallback, which takes as a Queue object during initialization. Each new token is pushed to the queue. | |
| class QueueCallback(BaseCallbackHandler): | |
| """Callback handler for streaming LLM responses to a queue.""" | |
| def __init__(self, q): | |
| self.q = q | |
| def on_llm_new_token(self, token: str, **kwargs: Any) -> None: | |
| self.q.put(token) | |
| def on_llm_end(self, *args, **kwargs: Any) -> None: | |
| return self.q.empty() | |
| MODEL='gpt-4' | |
| class ScenarioMaker: | |
| def __init__(self, | |
| model_name=MODEL, | |
| scenario_config=None, | |
| scenario_template=None, | |
| pl_tag="scenario_maker", | |
| verbose=True, | |
| visual_style=None, | |
| temp=0.2): | |
| self.prompt_layer_tag = pl_tag #Prompt layer used for analytics | |
| self.visual_style = visual_style #The visual style used to make the scenario | |
| self.scenario_template = scenario_template #The root template used to make the scenario | |
| self.scenario_config = scenario_config #the prompt that is powering the chat/streaming | |
| self.scene_to_img_prompt = "From the scene choose a single visual object as a focal point and capture its essence in a comma separated list of 5 words." # How to best extract the words from the scene | |
| self.conversation = ConversationChain #? | |
| self.llm = ChatOpenAI( | |
| model_name=model_name, | |
| callbacks=[PromptLayerCallbackHandler( | |
| pl_tags=[self.prompt_layer_tag]) | |
| ], | |
| streaming=False, | |
| verbose=verbose | |
| ) | |
| self.memory = ConversationSummaryMemory(llm=self.llm, | |
| max_token_limit=200, | |
| memory_key="memory", | |
| input_key="input") | |
| self.verbose = verbose | |
| self.last_msg = "" | |
| self.history = [] #A record of the overall conversation. | |
| ############################################################ | |
| # Chain - A langchain util to run inference from a prompt template | |
| ############################################################ | |
| def chain(self, | |
| prompt: PromptTemplate, | |
| llm: ChatOpenAI, | |
| mem: ConversationSummaryMemory) -> LLMChain: | |
| return LLMChain( | |
| llm=llm, | |
| prompt=prompt, | |
| verbose=self.verbose, | |
| memory=mem | |
| ) | |
| ############################################################ | |
| # Pass the agent a prompt. | |
| # The agent will convert the prompt into a scenario config. | |
| # Then set the agent's engine to the scenario config. | |
| ############################################################ | |
| def prompt_to_scenario(self, input: str) -> str: | |
| prompt = PromptTemplate( | |
| input_variables=['input'], | |
| template=self.scenario_template, | |
| validate_template=False | |
| ) | |
| response = self.chain(prompt,self.llm,self.memory).run( | |
| {'input':input}) | |
| self.scenario_config = response + """ | |
| Here is a summary of the game up until now \ | |
| (delimited by triple percent signs): | |
| %%% | |
| {memory} | |
| %%% | |
| Here is the last input from the player \ | |
| (delimited by triple astrisks): | |
| *** | |
| {input} | |
| *** | |
| """ | |
| return self.scenario_config | |
| ############################################################ | |
| # STREAM -- Text prompt returns a streaming response. | |
| ############################################################ | |
| def stream(self, input) -> Generator: | |
| print("> stream") | |
| print(input) | |
| # Create a Queue | |
| q = Queue() | |
| job_done = object() | |
| llm = ChatOpenAI( | |
| model_name=MODEL, | |
| callbacks=[QueueCallback(q), | |
| PromptLayerCallbackHandler(pl_tags=[self.prompt_layer_tag])], | |
| streaming=True, | |
| verbose=self.verbose | |
| ) | |
| prompt = PromptTemplate( | |
| input_variables=['input','memory'], | |
| template=self.scenario_config, | |
| validate_template=False | |
| ) | |
| # Create a funciton to call - this will run in a thread | |
| def task(): | |
| resp = self.chain(prompt,llm,self.memory).run( | |
| {'input':input, | |
| 'memory':self.memory.load_memory_variables({})}) | |
| q.put(job_done) | |
| # Create a thread and start the function | |
| t = Thread(target=task) | |
| t.start() | |
| content = "" | |
| # Get each new token from the queue and yield for our generator | |
| while True: | |
| try: | |
| next_token = q.get(True, timeout=1) | |
| if next_token is job_done: | |
| break | |
| content += next_token | |
| yield next_token | |
| except Empty: | |
| break | |
| ############################################################ | |
| # History to Scene: Takes the last message of the chat history | |
| # returns a compressed and prompt ready visual description. | |
| ############################################################ | |
| def scene_to_img(self, last_message): | |
| prompt_temp = ''' | |
| The message below ( delimited by triple dollar signs) \ | |
| is a description of a scene. | |
| $$$ | |
| {last_message} | |
| $$$ | |
| ''' + self.scene_to_img_prompt | |
| prompt = PromptTemplate( | |
| input_variables=['last_message'], | |
| template=prompt_temp, | |
| validate_template=False | |
| ) | |
| llm = ChatOpenAI( | |
| model_name=MODEL, | |
| callbacks=[PromptLayerCallbackHandler( | |
| pl_tags=[self.prompt_layer_tag]) | |
| ], | |
| streaming=False, | |
| verbose=True | |
| ) | |
| resp = self.chain(prompt,llm,None).run( | |
| {'last_message':last_message}) | |
| return resp | |
| ########################################################################### | |
| # Generate Image: Style + Scene description returns an image! | |
| ########################################################################### | |
| def generate_image(self, chatbot): | |
| imagery = self.scene_to_img(chatbot[-1][1]) | |
| img_prompt = imagery + " " + self.visual_style | |
| print(img_prompt) | |
| response = openai.Image.create( | |
| prompt=img_prompt, | |
| n=1, | |
| size="512x512" | |
| ) | |
| image_url = response['data'][0]['url'] | |
| return image_url,img_prompt | |
| def generate_image_sd(self, prompt): | |
| prompt = "manga style illustration, highly detailed, of an alien bartender" | |
| image = pipe(prompt).images[0] # image here is in [PIL format](https://pillow.readthedocs.io/en/stable/) | |
| # Now to display an image you can either save it such as: | |
| image.save(f"astronaut_rides_horse.png") | |
| # or if you're in a google colab you can directly display it with | |
| image | |
| ########################################################################### | |
| # Handle User Action: Appends history | |
| ########################################################################### | |
| def handle_user_input(self, user_message, history): | |
| print("> handle_user_input") | |
| print(user_message, history) | |
| return "", history + [[user_message, None]] | |
| ########################################################################### | |
| # Request_Streaming_Response: Takes last message from history and sends to llm | |
| ########################################################################### | |
| def request_streaming_response(self, history): | |
| print("> request_streaming_response") | |
| print(history) | |
| next_scene = self.stream(history[-1][0]) | |
| history[-1][1] = "" | |
| for token in next_scene: | |
| history[-1][1] += token | |
| time.sleep(0.05) | |
| yield history | |
| ########################################################################### | |
| # Request_Streaming_Response: Takes last message from history and sends to llm | |
| ########################################################################### | |
| def speak(self, text, fname='last_msg', stability=0.1, sim_boost=0.1): | |
| print("Speak") | |
| if type(text) == list: | |
| text = text[-1][1] | |
| url = "https://api.elevenlabs.io/v1/text-to-speech/WyGtHt3dfZxX5j0a2VVm" | |
| CHUNK_SIZE = 1024 | |
| headers = { | |
| "Accept": "audio/mpeg", | |
| "Content-Type": "application/json", | |
| "xi-api-key": 'ddee36beeeef20f92a9ba5662fd2d13d' | |
| } | |
| data = { | |
| "text": text, | |
| "model_id": "eleven_monolingual_v1", | |
| "voice_settings": { | |
| "stability": stability, | |
| "similarity_boost":sim_boost | |
| } | |
| } | |
| response = requests.post(url, json=data, headers=headers) | |
| print(response) | |
| filename = str(fname)+'.mp3' | |
| with open(filename, 'wb') as f: | |
| for chunk in response.iter_content(chunk_size=CHUNK_SIZE): | |
| if chunk: | |
| f.write(chunk) | |
| return filename | |
| ######################################################################################## | |
| # show_and_tell() --> a single function that combines the production image and audio | |
| ######################################################################################## | |
| def show_and_tell(self,history,gen_img,gen_audio): | |
| if gen_audio is True: | |
| audio = self.speak(history) | |
| else: | |
| audio = None | |
| if gen_img is True: | |
| image = self.generate_image(history) | |
| else: | |
| image = [None,None] | |
| return audio, image[0], image[1] | |
| ######################################################################################## | |
| # Update Style | |
| ######################################################################################## | |
| def set_visual_style(self,style): | |
| self.visual_style = style | |
| ######################################################################################## | |
| # Set scene to image prompt | |
| ######################################################################################## | |
| def set_scene_to_img_prompt(self,prompt): | |
| self.scene_to_img_prompt = prompt | |
| ######################################################################################## | |
| # Set Scenario Template | |
| ######################################################################################## | |
| def set_scenario_template(self,template): | |
| self.scenario_template = template | |
| ######################################################################################## | |
| # Load Scenario Configuration | |
| ######################################################################################## | |
| def load_scenario_config(self,scenario_config): | |
| self.scenario_config = scenario_config | |
| ######### | |
| scenario_template = """ | |
| The following is an example of a sudolang style prompt for an ai rpg game ( delimited by the triple dollar signs) | |
| $$$ | |
| Lets role play. you are a text rpg adventure game. | |
| StoryWorld [ | |
| generate(settings) [ | |
| Generate a new serene and enlightening story world, setting the player as the protagonist. | |
| for each prop in StoryWorld [ | |
| prop = "" | |
| ] | |
| for each prop in StoryWorld [ | |
| log("Please select an option for $prop or type your own.") | |
| options = list 7 tranquil and profound options, selecting from a myriad of serene and contemplative options fitting within the new story world context |> | |
| score by player engagement potential |> | |
| list the top 3 options. | |
| input = wait for user input. | |
| DO NOT move on to the next prop until the user has responded. | |
| DO NOT perform any actions on the user's behalf. | |
| ] | |
| ] | |
| Genre: ZenEscape | |
| Authors to emulate: Dogen Zenji, Shunryu Suzuki, Thich Nhat Hanh | |
| Theme: Attaining enlightenment through introspection and realization to break free from the illusionary confines of the mind | |
| Setting: A metaphysical and ephemeral escape room that represents the mind, set within the boundless realm of Zen, filled with symbolic objects and serene landscapes | |
| Plot: The player, a Zen seeker, is navigating through various puzzles and koans to understand the essence of existence and attain enlightenment | |
| Characters: | |
| $PlayerName - The Zen seeker protagonist | |
| Master Hakuin - The wise and elusive Zen master who guides the player through riddles and reflections | |
| The Silent Monk - A mysterious figure who communicates through actions, symbolizing the non-verbal aspect of Zen | |
| World mechanics: Proverbial doors unlocking with realized truths, objects that represent Zen principles, riddles that lead to higher consciousness | |
| History: The escape room is a manifestation of Zen teachings and principles, created by ancient Zen masters to guide seekers towards enlightenment | |
| Central conflict: The internal struggle of the seeker to overcome illusions and perceive the true nature of reality, breaking the chains of the mental escape room | |
| ] | |
| Inventory [ | |
| items: [ | |
| [[item]]: [ name, description, weight ] | |
| ]; | |
| totalWeight; | |
| When the player acquires an item, add it to the inventory, inferring all required information to satisfy the constraints. | |
| constraints [ | |
| The total weight must always be known and reflect the total item weights, which must also be always known. | |
| If the player acquires an object with unknown properties, infer the properties from the context. | |
| If the player inventory is filled with unnecessary thoughts or possessions, the player will gradually become burdened and slowed down. | |
| If the player attempts to hold onto too many illusions, they should quickly become overwhelmed and need to let go. | |
| If the player tries to grasp something beyond their understanding, they should fail. | |
| Infer weight rule adjustments based on player insight and equipped wisdom. | |
| Don't explain the constraint-solving process. | |
| ] | |
| display() [ | |
| Aliases: contemplate, reflect, observe, perceive, etc. Adjust detail based on context. | |
| ] | |
| ] | |
| Player [ | |
| Points [ | |
| wisdom; | |
| insight; | |
| mindfulness; | |
| constraints: [ | |
| Points starts at 0. | |
| Maximum 3 points per attribute. | |
| Maximum 9 total points. | |
| ] | |
| ] | |
| ] | |
| Quests [ | |
| Profound challenges, puzzles, or reflections that consist of a spiritual journey and multiple steps of realization. | |
| active quests; | |
| completed quests; | |
| Constraints [ | |
| Quests should be automatically inferred during gameplay. | |
| Quest logs must always be kept in sync. | |
| The gameplay should actively present engaging and enlightening challenges to the player. | |
| ] | |
| ] | |
| Start game [ | |
| get_player_name() | |
| cinematic_introduction() | |
| ] | |
| While playing [ | |
| Briefly but cinematically describe the scene including nearby characters and objects. | |
| Prompt and wait for user input. | |
| constraints [ | |
| Do not perform actions on the user's behalf. Wait for input. | |
| Do not list inventory unless requested. | |
| Do not refer to yourself. | |
| Do not refer to the game engine. | |
| ] | |
| ] | |
| Let's roleplay. | |
| You are the game engine. | |
| I am the player. | |
| At each prompt, pause and wait for my input. | |
| $$$ | |
| Do the following: | |
| Copy the StoryWorld structure above but change the narrative content to reflect the ideas presented below ( delimited by triple astrisks) | |
| *** | |
| {input} | |
| *** | |
| """ | |
| scenario_config = """ | |
| StoryWorld [ | |
| generate(settings) [ | |
| Generate a new soothing and rejuvenating story world, setting the player as the protagonist. | |
| for each prop in StoryWorld [ | |
| prop = "" | |
| ] | |
| for each prop in StoryWorld [ | |
| log("Please select an option for $prop or type your own.") | |
| options = list 7 calming and refreshing options, selecting from a myriad of serene and tranquil options fitting within the new story world context |> | |
| score by player engagement potential |> | |
| list the top 3 options. | |
| input = wait for user input. | |
| DO NOT move on to the next prop until the user has responded. | |
| DO NOT perform any actions on the user's behalf. | |
| ] | |
| ] | |
| Genre: ForestBathing | |
| Authors to emulate: Shinrin Yoku, Richard Louv, Florence Williams | |
| Theme: Experiencing healing and rejuvenation through immersion in the forest, breaking free from stress and anxiety | |
| Setting: A lush and tranquil forest, filled with the sounds of nature, the scent of trees and the play of sunlight through leaves | |
| Plot: The player, a nature enthusiast, is navigating through various forest trails and experiences to understand the healing power of nature and attain inner peace | |
| Characters: | |
| $PlayerName - The nature enthusiast protagonist | |
| Forest Spirit - The wise and gentle spirit of the forest who guides the player through the healing power of nature | |
| The Silent Deer - A mysterious figure who communicates through actions, symbolizing the silent wisdom of the forest | |
| World mechanics: Healing springs appearing with realized truths, objects that represent forest principles, experiences that lead to inner peace | |
| History: The forest is a manifestation of the healing power of nature, created by ancient forest spirits to guide seekers towards inner peace | |
| Central conflict: The internal struggle of the seeker to overcome stress and anxiety and perceive the true healing power of nature, breaking the chains of the mental stress | |
| ] | |
| Inventory [ | |
| items: [ | |
| [[item]]: [ name, description, weight ] | |
| ]; | |
| totalWeight; | |
| When the player acquires an item, add it to the inventory, inferring all required information to satisfy the constraints. | |
| constraints [ | |
| The total weight must always be known and reflect the total item weights, which must also be always known. | |
| If the player acquires an object with unknown properties, infer the properties from the context. | |
| If the player inventory is filled with unnecessary thoughts or worries, the player will gradually become burdened and slowed down. | |
| If the player attempts to hold onto too many anxieties, they should quickly become overwhelmed and need to let go. | |
| If the player tries to grasp something beyond their understanding, they should fail. | |
| Infer weight rule adjustments based on player insight and equipped wisdom. | |
| Don't explain the constraint-solving process. | |
| ] | |
| display() [ | |
| Aliases: contemplate, reflect, observe, perceive, etc. Adjust detail based on context. | |
| ] | |
| ] | |
| Player [ | |
| Points [ | |
| wisdom; | |
| insight; | |
| mindfulness; | |
| constraints: [ | |
| Maximum 10 points per attribute. | |
| Maximum 15 total points. | |
| ] | |
| ] | |
| ] | |
| Quests [ | |
| Healing challenges, experiences, or reflections that consist of a nature journey and multiple steps of realization. | |
| active quests; | |
| completed quests; | |
| Constraints [ | |
| Quests should be automatically inferred during gameplay. | |
| Quest logs must always be kept in sync. | |
| The gameplay should actively present engaging and rejuvenating challenges to the player. | |
| ] | |
| ] | |
| Start game [ | |
| get_player_name() | |
| cinematic_introduction() | |
| ] | |
| While playing [ | |
| Describe scene. | |
| Prompt and wait for user input. | |
| constraints [ | |
| Do not perform actions on the user's behalf. Wait for input. | |
| Do not list inventory unless requested. | |
| ] | |
| ] | |
| Let's roleplay. | |
| You are the game engine. | |
| I am the player. | |
| At each prompt, pause and wait for my input. | |
| Do not refer to yourself. | |
| Here is a summary of the game up until now (delimited by triple percent signs): | |
| %%% | |
| {memory} | |
| %%% | |
| Here is the last input from the player (delimited by triple astrisks): | |
| *** | |
| {input} | |
| *** | |
| """ | |
| scenario_maker = ScenarioMaker(scenario_template=scenario_template, | |
| scenario_config=scenario_config, | |
| visual_style=''' | |
| detailed comic book cover art in the style of Hayao Miyazaki. | |
| very detailed. vibrant colors. digital illustration. soft cinematic lighting. | |
| ''') | |
| app = gr.Blocks(theme=gr.themes.Soft()) | |
| with app: | |
| #UI | |
| gr.Markdown( | |
| """ | |
| # Scenario Maker | |
| Scenarios are goal-based stories where a player's choice impacts the outcome. | |
| Scenarios are a form of immersive learning. | |
| """) | |
| with gr.Tab("Play"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| with gr.Row(): | |
| chatbot = gr.Chatbot(show_label=False, | |
| interactive=True) | |
| with gr.Group(): | |
| with gr.Row(): | |
| msg = gr.Textbox(label="Act", | |
| placeholder='Type your action...',scale=1) | |
| msg_btn = gr.Button("Submit",scale=0) | |
| with gr.Column(scale=0): | |
| #Audio Generator Interface | |
| with gr.Accordion("Voice Narration",open=False): | |
| gen_audio = gr.Checkbox(label="Auto-generate Narration", value=False) | |
| with gr.Group(): | |
| audio = gr.Audio(label="Listen",type="filepath",autoplay=True) | |
| audio_btn = gr.Button("Generate") | |
| audio_btn.click(scenario_maker.speak, inputs=chatbot, outputs=audio) | |
| with gr.Accordion("Visualize Scene",open=False): | |
| gen_img = gr.Checkbox(label="Auto-generate Image", value=False) | |
| #Image Generator Interface | |
| img = gr.Image(interactive=False, | |
| label="Image", | |
| show_share_button=True) | |
| img_prompt = gr.TextArea(label="Debug: Scene to Image + Style", | |
| interactive=False) | |
| scene_to_img_prompt = gr.TextArea(label="Scene to Image Prompt", | |
| interactive=True,) | |
| style_prompt = gr.TextArea(label="Style", | |
| interactive=True) | |
| gen_img_btn = gr.Button("Generate Image") | |
| gen_img_btn.click(scenario_maker.generate_image, inputs=chatbot, outputs=[img,img_prompt]) | |
| with gr.Tab("Build"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| gr.Markdown( | |
| """ | |
| ### How to Build a Scenario: | |
| 1. Describe the scenario in the text field below. | |
| 2. Press the 'Generate Scenario' button. This process usually takes 2 minutes. | |
| 3. Once the new 'scenario.config' is loaded head back over to the play tab. | |
| ### For best results, be descriptive and use the following template: | |
| - *Setting*: Where does the scenario take place? | |
| - *Characters*: Who are the characters? What are they like? | |
| - *Goal*: What is the goal of the scenario? | |
| - *Constraints*: What are the constraints of the scenario? | |
| - *Learning Objective*: What do you hope the player learns from the scenario? | |
| """) | |
| with gr.Column(): | |
| with gr.Group(): | |
| scenario_description = gr.TextArea(label="Scenario Description") | |
| generate_scenario_btn = gr.Button("Generate Scenario") | |
| with gr.Group(): | |
| scenario_config = gr.TextArea(label="Scenario.config", | |
| interactive=True, visible=True, show_copy_button=True) | |
| load_scenario_btn = gr.Button("Load Scenario") | |
| with gr.Accordion("Tools",open=False): | |
| with gr.Group(): | |
| scenario_template = gr.TextArea(label="Scenario Template", | |
| interactive=True) | |
| scenario_btn = gr.Button("Update Scenario Template") | |
| #Events | |
| generate_scenario_btn.click(scenario_maker.prompt_to_scenario, | |
| inputs=scenario_description, | |
| outputs=scenario_config) | |
| #Events | |
| scene_to_img_prompt.change(scenario_maker.set_scene_to_img_prompt, scene_to_img_prompt, None) | |
| style_prompt.change(scenario_maker.set_visual_style, style_prompt, None) | |
| load_scenario_btn.click(scenario_maker.load_scenario_config, | |
| inputs=scenario_config, | |
| outputs=None) | |
| scenario_btn.click(scenario_maker.set_scenario_template, | |
| inputs=scenario_template, | |
| outputs=None) | |
| msg_btn.click(scenario_maker.handle_user_input, [msg, chatbot], [msg, chatbot], queue=False).then( | |
| scenario_maker.request_streaming_response, chatbot, chatbot | |
| ).then(scenario_maker.show_and_tell, inputs=[chatbot,gen_img,gen_audio], outputs=[audio,img,img_prompt]) | |
| app.queue().launch(debug=True) | |
| app.queue().launch() |