|
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"] |
|
openai_api_key = os.environ["openai_key"] |
|
|
|
|
|
client = OpenAI( |
|
|
|
|
|
api_key = openai_api_key |
|
) |
|
|
|
|
|
|
|
model = "gpt-4o-mini" |
|
|
|
|
|
|
|
|
|
|
|
API_URL = "https://api-inference.huggingface.co/models/stabilityai/stable-diffusion-3.5-large-turbo" |
|
|
|
bearer_key = "Bearer " + api_key |
|
headers = {"Authorization": bearer_key} |
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
def request_to_image_model(story): |
|
response = requests.post(API_URL, headers=headers, json=story) |
|
return response.content |
|
|
|
|
|
def create_image(prompt): |
|
try: |
|
image_bytes = request_to_image_model({ |
|
"inputs": prompt, |
|
"parameters": { |
|
|
|
|
|
"seed": random.randint(0, 100) |
|
} |
|
}) |
|
except (UnidentifiedImageError) as e: |
|
time.sleep(60) |
|
image_bytes = request_to_image_model({ |
|
"inputs": prompt, |
|
"parameters": { |
|
|
|
|
|
"seed": random.randint(0, 100) |
|
} |
|
}) |
|
|
|
image = Image.open(io.BytesIO(image_bytes)) |
|
|
|
if image.mode != 'RGB': |
|
image = image.convert('RGB') |
|
|
|
return image |
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
|
|
|
|
|
def generate_story(story_type, language): |
|
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}. You must reply in {language}."}) |
|
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] |
|
question = lines[1] |
|
answers = [i.strip() for i in lines[2].split('|')] |
|
messages.append({"role": "assistant", "content": "I am waiting for next command."}) |
|
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.Radio(choices=[language], interactive=False), gr.Button(interactive=False), image |
|
|
|
|
|
|
|
def continue_story(previous_story, selected_option, story_type, language): |
|
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] |
|
question = lines[1] |
|
answers = [i.strip() for i in lines[2].split('|')] |
|
messages.append({"role": "assistant", "content": "I am waiting for next command."}) |
|
story = previous_story + '\n\n' + next_story |
|
|
|
if language != "English": |
|
messages.append({"role": "user", "content": f"translate this story to english: {next_story}"}) |
|
next_story = chat_with_llm(model, messages) |
|
messages.append({"role": "assistant", "content": "I am waiting for next command."}) |
|
|
|
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: " + next_story + character_prompt |
|
else: |
|
image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + next_story |
|
image = create_image(image_prompt) |
|
return story, question, gr.Radio(choices=answers, interactive=True), image |
|
|
|
|
|
|
|
def end_story(previous_story, selected_option, story_type, language): |
|
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) |
|
|
|
|
|
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 |
|
|
|
if language != "English": |
|
messages.append({"role": "user", "content": f"translate this story to english: {end_story}"}) |
|
end_story = chat_with_llm(model, messages) |
|
messages.append({"role": "assistant", "content": "I am waiting for next command."}) |
|
|
|
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: " + end_story + character_prompt |
|
else: |
|
image_prompt = "Cartoon image, with bright colors and simple shapes, that describes this story: " + end_story |
|
image = create_image(image_prompt) |
|
return story, image |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
messages = [] |
|
|
|
|
|
|
|
with gr.Blocks() as game_ui: |
|
with gr.Row(): |
|
|
|
with gr.Column(scale=1): |
|
story = gr.Textbox(label="Story", interactive=False, lines=12) |
|
story_image = gr.Image() |
|
|
|
|
|
with gr.Column(scale=1): |
|
language = gr.Radio(label="Select language", choices=['Greek', 'English']) |
|
|
|
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") |
|
|
|
|
|
|
|
|
|
start_button.click(fn=generate_story, inputs=[story_type, language], outputs=[story, question, answers, story_type, language, start_button, story_image]) |
|
submit_button.click(fn=continue_story, inputs=[story, answers, story_type, language], outputs=[story, question, answers, story_image]) |
|
end_button.click(fn=end_story, inputs=[story, answers, story_type, language], outputs=[story, story_image]) |
|
|
|
|
|
|
|
|
|
game_ui.launch() |
|
|
|
|
|
|
|
|
|
|