Banjo Obayomi commited on
Commit
44d7807
·
1 Parent(s): 4a25cbf

init test

Browse files
Files changed (2) hide show
  1. app.py +250 -0
  2. requirements.txt +5 -0
app.py ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import os
3
+ import shutil
4
+ import time
5
+ import uuid
6
+ import zipfile
7
+
8
+ import boto3
9
+ import gradio as gr
10
+ import requests
11
+ from botocore.config import Config
12
+ from fastapi import FastAPI
13
+ from fastapi.staticfiles import StaticFiles
14
+
15
+ # Define paths
16
+ S3_BUCKET = os.environ.get("S3_BUCKET")
17
+ UPLOAD_DIR = "uploads"
18
+ OUTPUT_DIR = "static/output"
19
+
20
+ # Ensure base directories exist
21
+ os.makedirs(UPLOAD_DIR, exist_ok=True)
22
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
23
+
24
+ # Initialize AWS clients
25
+ s3_client = boto3.client("s3")
26
+ # Create Lambda client with increased timeout
27
+ lambda_client = boto3.client(
28
+ "lambda",
29
+ region_name="us-west-2",
30
+ config=Config(read_timeout=600, connect_timeout=600),
31
+ )
32
+
33
+
34
+ def handle_file_upload(fileobj):
35
+ if fileobj is not None:
36
+ file_path = os.path.join(UPLOAD_DIR, os.path.basename(fileobj.name))
37
+ shutil.copyfile(fileobj.name, file_path)
38
+ return file_path
39
+ return None
40
+
41
+
42
+ def upload_to_s3(file_path, s3_key):
43
+ s3_client.upload_file(file_path, S3_BUCKET, s3_key)
44
+ return f"s3://{S3_BUCKET}/{s3_key}"
45
+
46
+
47
+ def invoke_lambda_function(story_prompt, lore_file_s3_path):
48
+ payload = {
49
+ "story_prompt": story_prompt,
50
+ "lore_file": lore_file_s3_path,
51
+ }
52
+ try:
53
+ print("Building your game... This may take a few minutes.")
54
+ response = lambda_client.invoke(
55
+ FunctionName="renpy_builder",
56
+ InvocationType="RequestResponse",
57
+ Payload=json.dumps(payload),
58
+ )
59
+ response_payload = json.loads(response["Payload"].read())
60
+ print("Lambda response:", response_payload)
61
+ return response_payload
62
+ except Exception as e:
63
+ print(f"Error invoking Lambda: {str(e)}")
64
+ return None
65
+
66
+
67
+ # def invoke_lambda_function(story_prompt, lore_file_s3_path):
68
+ # payload = {"story_prompt": story_prompt, "lore_file": lore_file_s3_path}
69
+ # try:
70
+ # print("Invoking Lambda function...")
71
+ # response = lambda_client.invoke(
72
+ # FunctionName="renpy_builder",
73
+ # InvocationType="RequestResponse",
74
+ # Payload=json.dumps(payload),
75
+ # )
76
+ # print("Finished")
77
+
78
+ # # Parse the response
79
+ # response_payload = json.loads(response["Payload"].read())
80
+ # print("Lambda response:", response_payload)
81
+
82
+ # if response_payload.get("success", False):
83
+ # return response_payload["download_url"]
84
+ # else:
85
+ # raise Exception(response_payload.get("message", "Unknown error"))
86
+
87
+ # except Exception as e:
88
+ # print(f"Error invoking Lambda: {str(e)}")
89
+ # raise
90
+
91
+
92
+ def load_existing_game(session_id):
93
+ """Load an existing game by session ID"""
94
+ if not session_id:
95
+ return gr.HTML("Please enter a session ID")
96
+
97
+ # Check if the game exists
98
+ game_path = os.path.join(OUTPUT_DIR, session_id)
99
+ if not os.path.exists(os.path.join(game_path, "index.html")):
100
+ return gr.HTML(f"No game found with session ID: {session_id}")
101
+
102
+ # Generate a unique query parameter to force iframe reload
103
+ timestamp = int(time.time())
104
+
105
+ # Create HTML with iframe
106
+ gradio_html = f"""
107
+ <div style="display: flex; flex-direction: column; align-items: center;">
108
+ <iframe width="1280" height="720" src="/static/output/{session_id}/index.html?v={timestamp}"></iframe>
109
+ <p style="text-align: center; margin-top: 10px;">
110
+ Instructions: Use your mouse to interact with the game. Click to advance dialogue.
111
+ </p>
112
+ </div>
113
+ """
114
+
115
+ return gr.HTML(gradio_html)
116
+
117
+
118
+ def build_and_display_game(story_prompt, lore_file):
119
+ try:
120
+ # Generate a unique ID for this game session
121
+ session_id = str(uuid.uuid4())
122
+
123
+ # Handle file upload for lore file
124
+ lore_file_s3_path = None
125
+ if lore_file:
126
+ local_path = handle_file_upload(lore_file)
127
+ s3_key = f"lore_files/{session_id}/{os.path.basename(local_path)}"
128
+ lore_file_s3_path = upload_to_s3(local_path, s3_key)
129
+
130
+ # Call lambda function and get download URL
131
+ download_url = invoke_lambda_function(story_prompt, lore_file_s3_path)
132
+
133
+ if not download_url:
134
+ return gr.HTML(
135
+ "<p>Error: Failed to get download URL from Lambda function</p>"
136
+ )
137
+
138
+ # Download zip from the download_url
139
+ local_zip_path = f"/tmp/{session_id}_game.zip"
140
+ response = requests.get(download_url)
141
+ with open(local_zip_path, "wb") as f:
142
+ f.write(response.content)
143
+
144
+ # Unzip to /static/output/{session_id}/
145
+ output_path = os.path.join(OUTPUT_DIR, session_id)
146
+ with zipfile.ZipFile(local_zip_path, "r") as zip_ref:
147
+ zip_ref.extractall(output_path)
148
+
149
+ # Generate a unique query parameter to force iframe reload
150
+ timestamp = int(time.time())
151
+
152
+ # Create HTML with iframe
153
+ gradio_html = f"""
154
+ <div style="display: flex; flex-direction: column; align-items: center;">
155
+ <iframe width="1280" height="720" src="/app/output/{session_id}/index.html?v={timestamp}"></iframe>
156
+ <p style="text-align: center; margin-top: 10px;">
157
+ Instructions: Use your mouse to interact with the game. Click to advance dialogue.
158
+ </p>
159
+ </div>
160
+ """
161
+
162
+ return gr.HTML(gradio_html)
163
+
164
+ except Exception as e:
165
+ print(f"Error in build_and_display_game: {str(e)}")
166
+ return gr.HTML(f"<p>Error: {str(e)}</p>")
167
+
168
+
169
+ # Create Gradio interface
170
+ with gr.Blocks() as demo:
171
+ gr.Markdown("# VisualNovelLM")
172
+ gr.Markdown(
173
+ "Enter a prompt to generate a story, and optionally upload a document with additional lore."
174
+ )
175
+
176
+ with gr.Row():
177
+ story_prompt = gr.Textbox(
178
+ label="Enter story prompt",
179
+ placeholder="Describe the story you want to generate. Include details about the setting, main characters, and key plot points.",
180
+ lines=5,
181
+ )
182
+ lore_file = gr.File(label="Upload lore document (optional)")
183
+
184
+ # Add example prompts
185
+ gr.Examples(
186
+ examples=[
187
+ [
188
+ "Create a romantic comedy visual novel set in a bustling city. The main character is a young professional who accidentally swaps phones with their dream date. Include funny misunderstandings and heartwarming moments as they try to return the phone and potentially find love.",
189
+ None,
190
+ ],
191
+ [
192
+ "Generate a mystery visual novel set in a remote mountain village. The protagonist is a detective investigating a series of strange disappearances. Include red herrings, multiple suspects, and a surprising twist at the end.",
193
+ None,
194
+ ],
195
+ [
196
+ "Develop a sci-fi visual novel aboard a space station. The main character is a new crew member who discovers an alien artifact that grants them the ability to see glimpses of the future. Explore the ethical implications and potential dangers of this power.",
197
+ None,
198
+ ],
199
+ [
200
+ "Create a fantasy visual novel in a magic school setting. The protagonist is a student with unique abilities that make them an outcast. Include scenes of learning magic, making friends, and ultimately saving the school from a ancient threat.",
201
+ None,
202
+ ],
203
+ [
204
+ "Generate a historical visual novel set during the Renaissance in Florence. The main character is an apprentice artist trying to make a name for themselves. Include interactions with famous historical figures and a plot involving art forgery and political intrigue.",
205
+ None,
206
+ ],
207
+ ],
208
+ inputs=[story_prompt, lore_file],
209
+ label="Example Prompts",
210
+ cache_examples=False, # Enable caching for examples
211
+ outputs=gr.HTML(label="Game Output"),
212
+ fn=build_and_display_game,
213
+ run_on_click=False,
214
+ )
215
+
216
+ generate_button = gr.Button("Generate Game")
217
+
218
+ output = gr.HTML(label="Game Output")
219
+
220
+ generate_button.click(
221
+ fn=build_and_display_game, inputs=[story_prompt, lore_file], outputs=output
222
+ )
223
+
224
+ with gr.Tab("Load Existing Game"):
225
+ gr.Markdown("Enter a session ID to load an existing game")
226
+
227
+ session_id_input = gr.Textbox(
228
+ label="Session ID", placeholder="Enter the session ID of an existing game"
229
+ )
230
+ load_button = gr.Button("Load Game")
231
+ load_output = gr.HTML(label="Game Output")
232
+
233
+ load_button.click(
234
+ fn=load_existing_game, inputs=session_id_input, outputs=load_output
235
+ )
236
+
237
+ # Create FastAPI app
238
+ app = FastAPI()
239
+
240
+ # Mount static files
241
+ app.mount("/static", StaticFiles(directory="static", html=True), name="static")
242
+
243
+ # Mount Gradio app
244
+ app = gr.mount_gradio_app(app, demo, "/")
245
+
246
+ # Run the app
247
+ if __name__ == "__main__":
248
+ import uvicorn
249
+
250
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ gradio
2
+ fastapi
3
+ uvicorn
4
+ boto3
5
+ pillow