from openai import OpenAI import gradio as gr import requests import matplotlib.pyplot as plt import io from PIL import Image, UnidentifiedImageError import random import time import os #------------------------------------------------------------------------------------------------ api_key = os.environ["genai_stories"] # define LLM model for story creation, with OpenAI's format client = OpenAI( base_url="https://api-inference.huggingface.co/v1/", api_key=api_key ) # the model to utilize model = "mistralai/Mixtral-8x7B-Instruct-v0.1" # define model for image creation # API_URL = "https://api-inference.huggingface.co/models/black-forest-labs/FLUX.1-dev" # API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-xl-base-1.0" # API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-3.5-large" API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-3.5-large-turbo" bearer_key = "Bearer " + api_key headers = {"Authorization": bearer_key} #------------------------------------------------------------------------------------------------ # connect with the LLM and create responses def chat_with_llm(model, messages): completion = client.chat.completions.create( model=model, messages=messages, max_tokens=2048, temperature=0.7, top_p=0.98, seed=random.randint(0, 100)) return completion.choices[0].message.content #------------------------------------------------------------------------------------------------ # connect with image model and create images def request_to_image_model(story): response = requests.post(API_URL, headers=headers, json=story) return response.content # create the final image, adjusting the model's hyperparameters and converting to RGB def create_image(prompt): try: image_bytes = request_to_image_model({ "inputs": prompt, "parameters": { # "num_inference_steps": 50, # Optional: controls generation quality # "guidance_scale": 7.5, # Optional: controls adherence to the prompt "seed": random.randint(0, 100) # Set the seed to a random number } }) except (UnidentifiedImageError) as e: time.sleep(60) image_bytes = request_to_image_model({ "inputs": prompt, "parameters": { # "num_inference_steps": 50, # Optional: controls generation quality # "guidance_scale": 7.5, # Optional: controls adherence to the prompt "seed": random.randint(0, 100) # Set the seed to a random number } }) image = Image.open(io.BytesIO(image_bytes)) # Convert the image to RGB if it's not if image.mode != 'RGB': image = image.convert('RGB') return image #------------------------------------------------------------------------------------------------ # create a prompt for the image model based on the character def create_character_prompt(story_type): main_character = story_type.split(' ')[2] if main_character == 'king': character_prompt = " The " + main_character + " has a strong jawline and piercing eyes, dark hair, shining silver armor with simple decorations, red cape falling behind him, large sword." elif main_character == 'alien': character_prompt = " The " + main_character + " has a round, shiny head, big eyes glow bright blue, small and green with tiny arms, silver spacesuit covers its body." elif main_character == 'rabbit': character_prompt = " The " + main_character + " has soft white fur, big ears stand up tall, wears a tiny red bow tie, fluffy tail bounces as it hops." elif main_character == 'princess': character_prompt = " The " + main_character + " has long brown hair, sparkling blue eyes shine brightly, wears a flowing pink gown, small jeweled crown rests on her head." return character_prompt #------------------------------------------------------------------------------------------------ # Initialize the story based on the story_type selected by the player def generate_story(story_type): global model, messages messages = [] messages.append({"role": "system", "content": f"You are a structured storytelling assistant. Your purpose is to generate stories, questions, and answers in a specific format. Always adhere strictly to the rules provided by the user. This is the topic that you must create a story about: story_type = {story_type}"}) messages.append({"role": "user", "content": f"You must create and return one story part, one question and four answers. To do that, you must explicitly follow these steps: Step 1) Create the initial part of the story, according to the story_type from your system content, within 50 and 60 words, without switching to new line. Then you must switch line by adding this: '\n\n'. Step 2) Create a question, within 10 and 20 words, on how to proceed the story from step 1. Then you must switch line by adding this: '\n\n'. Step 3) Create the 4 potential answers for the question in step 2. The answers of the question must be given in the format: '1:... | 2:... | 3:... | 4:...'. Do not change this format and do not add any new lines between the answers. Every answer must be maximum 20 words. All answers must be separated from each other with '|'. Now some general guidelines for your response: 1) Don't explicitly specify 'Story', 'Question', or 'Answer'. 2) You must ALWAYS reply in this format: '[story from step 1]\n\n[question from step 2]\n\n[answers from step 3]'. 3) Do not return any other stuff in your response. 4) Always change lines with '\n\n' between ther story part and question and between the question and the answers."}) messages = [messages[0]] + [messages[-1]] response = chat_with_llm(model, messages) lines = response.split("\n\n") story = lines[0] # Everything before the last two lines is the story question = lines[1] # The second to last line is the question answers = [i.strip() for i in lines[2].split('|')] # The last line contains answers separated by '|' messages.append({"role": "assistant", "content": "I am waiting for next command."}) # main_character = story_type.split(' ')[2] character_prompt = create_character_prompt(story_type) image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + story + character_prompt image = create_image(image_prompt) return story, question, gr.Radio(choices=answers, interactive=True), gr.Radio(choices=[story_type], interactive=False), gr.Button(interactive=False), image # Continue the story based on what happened so far and the player's latest answer def continue_story(previous_story, selected_option, story_type): global model, messages messages.append({"role": "user", "content": f"You must create and return one story part, one question and four answers. To do that, you must explicitly follow these steps: Step 1) Based on this story so far: '{previous_story} {selected_option}', continue the story and create the next part of the story within 50 and 60 words, without changing lines. You must provide ONLY the new part that you created. Then you must switch line by adding this: '\n\n'. Step 2) Create a question, within 10 and 20 words, on how to proceed the story from step 1. Then you must switch line by adding this: '\n\n'. Step 3) Create the 4 potential answers for the question in step 2. The answers of the question must be given in the format: '1:... | 2:... | 3:... | 4:...'. Do not change this format and do not add any new lines between the answers. Every answer must be maximum 20 words. All answers must be separated from each other with '|'. Now some general guidelines for your response: 1) Don't explicitly specify 'Story', 'Question', or 'Answer'. 2) You must ALWAYS reply in this format: '[story from step 1]\n\n[question from step 2]\n\n[answers from step 3]'. 3) Do not return any other stuff in your response. 4) Always change lines with '\n\n' between story part and question that you generate. 5) Always change lines with '\n\n' between question and the answers that you generate"}) messages = [messages[0]] + [messages[-1]] response = chat_with_llm(model, messages) lines = response.split("\n\n") next_story = lines[0] # Everything before the last two lines is the story question = lines[1] # The second to last line is the question answers = [i.strip() for i in lines[2].split('|')] # The last line contains answers separated by '|' messages.append({"role": "assistant", "content": "I am waiting for next command."}) story = previous_story + '\n\n' + next_story # image_prompt = "Cartoon image of: " + next_story + ". The image has bright colors and simple shapes, intended for children story." main_character = story_type.split(' ')[2].lower() character_prompt = create_character_prompt(story_type) if main_character in next_story.lower(): image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + story + character_prompt else: image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + story image = create_image(image_prompt) return story, question, gr.Radio(choices=answers, interactive=True), image # End the story based on what happened so far and the player's latest answer def end_story(previous_story, selected_option, story_type): global model, messages messages.append({"role": "user", "content": f"You must create an ending for this story: '{previous_story}' and also considering the latest answer: '{selected_option}'. You must provide only the ending of the story in an exciting way. Do not return any other stuff in your response."}) end_story = chat_with_llm(model, messages) # lines = response.split("\n\n") # end_story = lines[0] # Everything before the last two lines is the story messages.append({"role": "assistant", "content": "I ended the story successfully. Now I am not waiting for any more responses from the player."}) story = previous_story + '\n\n' + end_story # image_prompt = "Cartoon image of " + end_story + ". The image has bright colors and simple shapes, intended for children story." main_character = story_type.split(' ')[2].lower() character_prompt = create_character_prompt(story_type) if main_character in end_story.lower(): image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + story + character_prompt else: image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + story image = create_image(image_prompt) return story, image # Function to handle resetting the app # def reset_app(): # # Return empty values for all outputs # # global messages # # messages = [] # return "", "", "", gr.Radio(choices=["A knight who leads an army attacking a castle", # "A kid alien who lands on earth and explores around", # "Animals who participate in song contest", # "A little princess who is saved by a prince"], interactive=True), gr.Button(interactive=True) # initialize messages messages = [] # design the UI with gr.Blocks() as game_ui: with gr.Row(): # Left column for the story with gr.Column(scale=1): story = gr.Textbox(label="Story", interactive=False, lines=12) story_image = gr.Image() # height=256, width=256 # Right column for the question, answers, and buttons with gr.Column(scale=1): story_type = gr.Radio(label="What story to create?", choices=["A fearless king who leads an army attacking a castle.", "A kid alien who lands on earth and explores around.", "A joyful rabbit who participates in song contest.", "A beautiful princess who tries to find her way home." ]) start_button = gr.Button("Start Game!") question = gr.Textbox(label="Question", interactive=False) answers = gr.Radio(label="Choose an answer", choices=[]) submit_button = gr.Button("Submit your answer") end_button = gr.Button("End the story") # reset_button = gr.Button("Reset and play again") # what the buttons do start_button.click(fn=generate_story, inputs=[story_type], outputs=[story, question, answers, story_type, start_button, story_image]) submit_button.click(fn=continue_story, inputs=[story, answers, story_type], outputs=[story, question, answers, story_image]) end_button.click(fn=end_story, inputs=[story, answers, story_type], outputs=[story, story_image]) # reset_button.click(fn=reset_app, inputs=[], outputs=[story, question, answers, story_type, start_button]) # Launch the Gradio interface game_ui.launch() # game_ui.launch(share=True)