File size: 5,717 Bytes
0974473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520c904
0974473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520c904
0974473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
520c904
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0974473
 
 
 
 
520c904
0974473
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
import asyncio
import streamlit as st
from dotenv import load_dotenv
import os
import random

from middle_earth_adventure.constants import ALL_NAMES, ALL_SKILLS, ALL_TYPES, TEXT_MODEL, AUDIO_MODEL
from middle_earth_adventure.game_core import GameCore
from middle_earth_adventure.prompts import IMAGE_PROMPT
from middle_earth_adventure.utils import are_all_options_are_filled, check_valid_player, pick_rand_index, pick_rand_items
from middle_earth_adventure.schemas import Player, TechSpecs

################ BACKEND CODE ################
load_dotenv()
key = os.environ.get("OPENAI_PERSONAL_KEY")

# state-variables initialization 
if "text_area_value" not in st.session_state:
    st.session_state.text_area_value = "Choose you character..."
if "player" not in st.session_state:
    st.session_state.player = None
if "tech_specs" not in st.session_state:
    st.session_state.tech_specs = TechSpecs(narrator_voice="nova", image_model="", game_lenght=0)
if "image" not in st.session_state:
    st.session_state.image = "resources/intro.jpg"
if "narrator_audio"not in st.session_state:
    st.session_state.narrator_audio = None
if "game"not in st.session_state:
    st.session_state.game = GameCore(api_key=key, text_model=TEXT_MODEL, tts_model=AUDIO_MODEL)
if "game_iteration" not in st.session_state:
    st.session_state.game_iteration = 0
if "rand" not in st.session_state:
    st.session_state.rand = random.random()

game = st.session_state.game

async def progress_game(text_to_write, selection=None, start=False):
    with st.spinner('Loading...'):
        # utils
        player = st.session_state.player
        tech_specs = st.session_state.tech_specs
        st.session_state.game_iteration += 1 # count game rounds
        check_valid_player(player=st.session_state.player)
        # write text
        st.write(text_to_write)
        # Chat completion
        if start:
            narration_txt = await game.start_adventure(player=player)
        elif st.session_state.game_iteration < tech_specs.game_lenght:
            narration_txt = await game.continue_adventure(player=player, selection=selection)
        elif st.session_state.game_iteration == tech_specs.game_lenght:
            narration_txt = await game.finish_adventure(player=player, selection=selection)
        else: 
            narration_txt = "Game has ended. Thanks for playing!"
        # update
        st.session_state.text_area_value = narration_txt # update

        # Text to Speech
        mp3_audio_bytes = await game.narrate_adventure_out_loud(narration_txt, tech_specs.narrator_voice)
        st.session_state.narrator_audio = mp3_audio_bytes # update
        
        # Text to image generation
        prompt = IMAGE_PROMPT.format(narration=narration_txt, response_format='b64_json',name=name, sex=sex, type=character_type)
        image_url = await game.generate_picture_of_the_adventure(prompt, tech_specs.image_model)
        st.session_state.image = image_url 
        
        # Re-run to update states
        st.rerun()

default_name = ALL_NAMES[pick_rand_index(ALL_NAMES)]
default_type = pick_rand_index(ALL_TYPES)
default_skills = pick_rand_items(ALL_SKILLS, 2)

################ USER INTERFACE (Streamlit) ################

# Title
st.title("Middle Earth Adventures")


# Character and Game Selection
with st.form("selection_form"):
    # Tech Specs
    with st.expander("Technical Specs", expanded=False):
        narrator_voice = st.radio("Narrator's Voice", ["nova", "echo"], index=0)
        image_model = st.radio("Image Model", ['dall-e-2', 'dall-e-3'], index=1)
        game_lenght = st.selectbox("Game Lenght (nr of conversation turns)", [5, 7, 10, 15, 20], index=2)

    # Character Selection
    with st.expander("Character Selection", expanded=True):
        name = st.text_input("Name", value=default_name)
        character_type = st.selectbox("Type", ALL_TYPES, index=default_type)
        sex = st.radio("Gender", ["she", "he"], index=0)
        skills = st.multiselect("Skills (pick 2)", ALL_SKILLS, max_selections=2, help="")
        submit_button = st.form_submit_button("Create Character", use_container_width=True)
    if submit_button:
        # write player
        player = Player(name=name, type=character_type, sex=sex, skills=skills)
        st.session_state.player = player
        # write tech-specs
        tech_specs = TechSpecs(narrator_voice=narrator_voice, image_model=image_model, 
                            game_lenght=game_lenght)
        st.session_state.tech_specs = tech_specs
        # start adventure
        message = f"You are {name}, {sex} is a {character_type}. Your are good at {' and '.join(skills)}"
        if are_all_options_are_filled(player, name, character_type, sex, skills):
            asyncio.run(progress_game(message, start=True))
        else:
            st.toast("Invalid character definition!")

# Story Image
st.image(st.session_state.image, use_column_width=True)

# Story text
st.markdown(f'{st.session_state.text_area_value}')

# Narrator's Audio
st.audio(st.session_state.narrator_audio, format="audio/mp3", start_time=0, loop=False, autoplay=False)

# Action Buttons
col1, col2, col3 = st.columns(3)
with col1:
    if st.button("A", use_container_width=True) and check_valid_player(st.session_state.player):
        asyncio.run(progress_game("You chose option A", selection="A"))
with col2:
    if st.button("B", use_container_width=True) and check_valid_player(st.session_state.player):
        asyncio.run(progress_game("You chose option B", selection="B"))
with col3:
    if st.button("C", use_container_width=True) and check_valid_player(st.session_state.player):
        asyncio.run(progress_game("You chose option C", selection="C"))