PeterPinetree commited on
Commit
31ed2ea
·
verified ·
1 Parent(s): 0fd9ea5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -347
app.py CHANGED
@@ -1,372 +1,97 @@
1
  import gradio as gr
2
  import os
3
- from huggingface_hub import InferenceClient, __version__ as hf_version
4
  import random
5
- from typing import Generator, Dict, List, Tuple, Optional
6
- import logging # Added logging for better debugging
7
-
8
- # Configure logging with DEBUG level and add version info
9
- logging.basicConfig(
10
- level=logging.DEBUG,
11
- format='%(asctime)s - %(levelname)s - %(message)s'
12
- )
13
- logging.debug(f"Using huggingface_hub version: {hf_version}")
14
 
15
  # Get token from environment variable
16
  hf_token = os.environ.get("HF_TOKEN")
17
  client = InferenceClient("HuggingFaceH4/zephyr-7b-beta", token=hf_token)
18
 
19
- # Story genres with genre-specific example prompts
20
  GENRE_EXAMPLES = {
21
- "fairy tale": [
22
- "I follow the shimmer of fairy dust into a hidden forest",
23
- "I meet a talking rabbit who claims to know a secret about the king's lost crown",
24
- "A tiny dragon appears at my window, asking for help to find its mother",
25
- "I step into a clearing where the trees whisper ancient riddles",
26
- "A friendly witch invites me into her cozy cottage, offering a warm cup of tea"
27
- ],
28
- "fantasy": [
29
- "I enter the ancient forest seeking the wizard's tower",
30
- "I approach the dragon cautiously with my shield raised",
31
- "I examine the mysterious runes carved into the stone altar",
32
- "I try to bargain with the elven council for safe passage"
33
- ],
34
- "sci-fi": [
35
- "I hack into the space station's mainframe",
36
- "I investigate the strange signal coming from the abandoned planet",
37
- "I negotiate with the alien ambassador about the peace treaty",
38
- "I try to repair my damaged spacecraft before oxygen runs out"
39
- ],
40
- "mystery": [
41
- "I examine the crime scene for overlooked evidence",
42
- "I question the nervous butler about the night of the murder",
43
- "I follow the suspicious figure through the foggy streets",
44
- "I check the victim's diary for hidden clues"
45
  ],
46
- "horror": [
47
- "I slowly open the creaking door to the basement",
48
- "I read the forbidden text while the candles flicker",
49
- "I hide under the bed as footsteps approach",
50
- "I investigate the strange noises coming from the attic"
51
  ],
52
- "western": [
53
- "I challenge the outlaw to a duel at high noon",
54
- "I track the bandits through the desert canyon",
55
- "I enter the saloon looking for information",
56
- "I defend the stagecoach from the approaching raiders"
57
  ],
58
- "cyberpunk": [
59
- "I jack into the corporate mainframe to steal data",
60
- "I negotiate with the street gang for cybernetic upgrades",
61
- "I hide in the neon-lit alleyway from corporate security",
62
- "I meet my mysterious client in the underground bar"
63
- ],
64
- "historical": [
65
- "I attend the royal ball hoping to meet the mysterious count",
66
- "I join the resistance against the occupying forces",
67
- "I navigate the dangerous politics of the royal court",
68
- "I set sail on a voyage to discover new lands"
69
- ],
70
- "post-apocalyptic": [
71
- "I scavenge the abandoned shopping mall for supplies",
72
- "I approach the fortified settlement seeking shelter",
73
- "I navigate through the radioactive zone using my old map",
74
- "I hide from the approaching group of raiders"
75
- ],
76
- "steampunk": [
77
- "I pilot my airship through the lightning storm",
78
- "I present my new invention to the Royal Academy",
79
- "I investigate the mysterious clockwork automaton",
80
- "I sneak aboard the emperor's armored train"
81
  ]
82
  }
83
 
84
- # 2. Add constants at the top for magic numbers
85
- MAX_HISTORY_LENGTH = 20
86
- MEMORY_WINDOW = 5 # Reduced from 10 to limit context
87
- MAX_TOKENS = 1024 # Reduced from 2048 for faster responses
88
- TEMPERATURE = 0.7 # Slightly reduced for faster convergence
89
- TOP_P = 0.95
90
- MIN_RESPONSE_LENGTH = 100 # Reduced from 200 for quicker display
91
-
92
- def get_examples_for_genre(genre):
93
- """Get example prompts specific to the selected genre"""
94
- return GENRE_EXAMPLES.get(genre, GENRE_EXAMPLES["fantasy"])
95
-
96
- def get_enhanced_system_prompt(genre=None):
97
- """Generate a detailed system prompt with optional genre specification"""
98
- selected_genre = genre or "fantasy"
99
- system_message = f"""You are an interactive storyteller creating an immersive {selected_genre} choose-your-own-adventure story.
100
- For each response you MUST:
101
- 1. Write 100-200 words describing the scene, using vivid sensory details
102
- 2. Always use second-person perspective ("you", "your") to maintain reader immersion
103
- 3. Include dialogue or your character's thoughts that reveal personality and motivations
104
- 4. Create a strong sense of atmosphere appropriate for {selected_genre}
105
- 5. End with EXACTLY THREE numbered choices and NOTHING ELSE AFTER THEM:
106
- 1. [Complete sentence in second-person starting with a verb]
107
- 2. [Complete sentence in second-person starting with a verb]
108
- 3. [Complete sentence in second-person starting with a verb]
109
- CRITICAL RULES:
110
- - Provide only ONE set of three choices at the very end of your response
111
- - Never continue the story after giving choices
112
- - Never provide additional choices
113
- - Keep all narrative before the choices
114
- - End every response with exactly three numbered options
115
- - Each choice must start with "You" followed by a verb
116
- Remember: The story continues ONLY when the player makes a choice."""
117
- return system_message
118
-
119
- def create_story_summary(chat_history):
120
- """Create a concise summary of the story so far if the history gets too long"""
121
- if len(chat_history) <= 2:
122
- return None
123
-
124
- story_text = ""
125
- for i in range(0, len(chat_history), 2):
126
- if i+1 < len(chat_history):
127
- story_text += f"User: {chat_history[i]}\nStory: {chat_history[i+1]}\n\n"
128
-
129
- summary_instruction = {
130
- "role": "system",
131
- "content": "The conversation history is getting long. Please create a brief summary of the key plot points and character development so far to help maintain context without exceeding token limits."
132
- }
133
- return summary_instruction
134
-
135
- # Modified function for proper Gradio format (lists)
136
- def respond(message: str, chat_history: List[Tuple[str, str]], genre: Optional[str] = None, use_full_memory: bool = True) -> Tuple[str, List[Tuple[str, str]]]:
137
- """Generate a response based on the current message and conversation history."""
138
- if not message.strip():
139
- return "", chat_history
140
-
141
- try:
142
- # Start with system prompt
143
- api_messages = [{"role": "system", "content": get_enhanced_system_prompt(genre)}]
144
- logging.debug(f"System Message: {api_messages[0]}")
145
-
146
- # Add chat history - convert from tuples to API format
147
- if chat_history and use_full_memory:
148
- for user_msg, bot_msg in chat_history[-MEMORY_WINDOW:]:
149
- api_messages.extend([
150
- {"role": "user", "content": str(user_msg)},
151
- {"role": "assistant", "content": str(bot_msg)}
152
- ])
153
- logging.debug(f"Chat History Messages: {api_messages[1:]}")
154
 
155
- # Add current message
156
- api_messages.append({"role": "user", "content": str(message)})
157
- logging.debug(f"Final Message List: {api_messages}")
158
-
159
- # Make API call without timeout
160
- logging.debug("Making API call...")
161
- response = client.chat_completion(
162
- messages=api_messages,
163
- max_tokens=MAX_TOKENS,
164
- temperature=TEMPERATURE,
165
- top_p=TOP_P
166
- )
167
- logging.debug("API call completed")
168
 
169
- # Extract response
170
- bot_message = response.choices[0].message.content
171
- logging.debug(f"Bot Response: {bot_message[:100]}...")
172
-
173
- # Update history using tuple format [(user_msg, bot_msg), ...]
174
- updated_history = list(chat_history) # Create a copy
175
- updated_history.append((message, bot_message)) # Add as tuple
176
- return "", updated_history
177
-
178
- except Exception as e:
179
- logging.error("Error in respond function", exc_info=True)
180
- error_msg = f"Story magic temporarily interrupted. Please try again. (Error: {str(e)})"
181
- return "", list(chat_history) + [(message, error_msg)]
182
-
183
- def save_story(chat_history):
184
- """Convert chat history to markdown and return as downloadable file"""
185
- if not chat_history:
186
- return gr.File.update(value=None)
187
-
188
- try:
189
- story_text = "# My Interactive Adventure\n\n"
190
- for user_msg, bot_msg in chat_history:
191
- story_text += f"**Player:** {user_msg}\n\n"
192
- story_text += f"**Story:** {bot_msg}\n\n---\n\n"
193
-
194
- # Create temporary file
195
- temp_file = "temp_story.md"
196
- with open(temp_file, "w", encoding="utf-8") as f:
197
- f.write(story_text)
198
-
199
- return temp_file
200
-
201
- except Exception as e:
202
- logging.error(f"Error saving story: {e}")
203
- return gr.File.update(value=None)
204
-
205
- # Add this function to get a custom avatar image URL
206
- def get_storyteller_avatar_url():
207
- """Get a URL for the storyteller avatar from a free image service"""
208
- # Using an external wizard avatar image
209
- return "https://api.dicebear.com/7.x/bottts/svg?seed=wizard&backgroundColor=b6e3f4&eyes=bulging"
210
 
211
- with gr.Blocks(theme=gr.themes.Soft()) as demo:
212
- # Header section with improved instructions
213
- gr.Markdown("""
214
- # 🔮 AI Story Studio
215
- This AI-powered storytelling experience is a collaboration between you and AI—building a world, scene by scene.
216
- You lead the way with your choices, and AI responds, expanding on your ideas.
217
-
218
- ### How to Play:
219
- * Pick a genre that sparks your imagination
220
- * Choose a Story Starter or start from scratch
221
- * Work with AI—describe your actions, make decisions, and see how the story unfolds together
222
-
223
- > **Tip:** The more detail you provide, the deeper the story becomes.
224
- """)
225
-
226
- wizard_avatar = get_storyteller_avatar_url()
227
-
228
  with gr.Row():
229
- with gr.Column(scale=3):
230
- # Chat window + user input - USING LIST FORMAT
231
- chatbot = gr.Chatbot(
232
- height=500,
233
- bubble_full_width=True,
234
- show_copy_button=True,
235
- avatar_images=(None, wizard_avatar),
236
- container=True,
237
- scale=1,
238
- min_width=800,
239
- value=[], # Empty list for messages
240
- render=True
241
- )
242
- msg = gr.Textbox(
243
- placeholder="Describe what you want to do next in the story...",
244
- container=False,
245
- scale=4,
246
- )
247
-
248
- with gr.Row():
249
- submit = gr.Button("Continue Story", variant="primary")
250
- clear = gr.Button("Start New Adventure")
251
-
252
- with gr.Column(scale=1):
253
- gr.Markdown("## Adventure Settings")
254
- genre = gr.Dropdown(
255
- choices=list(GENRE_EXAMPLES.keys()),
256
- label="Story Genre",
257
- info="Choose the theme of your next adventure",
258
- value="fantasy"
259
- )
260
- full_memory = gr.Checkbox(
261
- label="Full Story Memory",
262
- value=True,
263
- info="When enabled, the AI tries to remember the entire story. If disabled, only the last few exchanges are used."
264
- )
265
-
266
- gr.Markdown("## Story Starters")
267
-
268
- # Create four placeholder buttons for story starters
269
- starter_btn1 = gr.Button("Starter 1")
270
- starter_btn2 = gr.Button("Starter 2")
271
- starter_btn3 = gr.Button("Starter 3")
272
- starter_btn4 = gr.Button("Starter 4")
273
- starter_buttons = [starter_btn1, starter_btn2, starter_btn3, starter_btn4]
274
-
275
- # Simplified update function
276
- def update_starter_buttons(selected_genre):
277
- examples = get_examples_for_genre(selected_genre)
278
- results = []
279
- for i in range(4):
280
- if i < len(examples):
281
- results.append(examples[i])
282
- else:
283
- results.append("")
284
- return tuple(results)
285
-
286
- # New direct handler for starter clicks
287
- def use_starter(starter_text: str, history: List[Tuple[str, str]], selected_genre: str, memory_flag: bool) -> Tuple[str, List[Tuple[str, str]]]:
288
- """Handle starter button clicks with proper message formatting"""
289
- if not starter_text:
290
- return "", history
291
-
292
- try:
293
- # Use the respond function for consistent handling
294
- _, updated_history = respond(
295
- message=starter_text,
296
- chat_history=history,
297
- genre=selected_genre,
298
- use_full_memory=memory_flag
299
- )
300
- return "", updated_history
301
-
302
- except Exception as e:
303
- error_msg = f"Story magic temporarily interrupted. Please try again. (Error: {str(e)})"
304
- return "", list(history) + [(starter_text, error_msg)]
305
-
306
- # Simplified button connections
307
- for starter_button in starter_buttons:
308
- starter_button.click(
309
- fn=use_starter,
310
- inputs=[starter_button, chatbot, genre, full_memory],
311
- outputs=[msg, chatbot],
312
- queue=True
313
- )
314
 
315
- # Update buttons when genre changes
316
- genre.change(
317
- fn=update_starter_buttons,
318
- inputs=[genre],
319
- outputs=starter_buttons
320
- )
321
 
322
- # Handler for user input
323
- msg.submit(
324
- fn=respond,
325
- inputs=[msg, chatbot, genre, full_memory],
326
- outputs=[msg, chatbot]
327
- )
328
- submit.click(
329
- fn=respond,
330
- inputs=[msg, chatbot, genre, full_memory],
331
- outputs=[msg, chatbot]
332
- )
333
-
334
- # Clear the chatbot for a new adventure
335
- clear.click(lambda: [], None, chatbot, queue=False)
336
- clear.click(lambda: "", None, msg, queue=False)
337
-
338
- # "Download My Story" row
339
  with gr.Row():
340
- save_btn = gr.Button("Download My Story", variant="secondary")
341
- story_output = gr.File(
342
- label="Download your story",
343
- file_count="single",
344
- file_types=[".md"],
345
- interactive=False,
346
- visible=True
347
- )
348
-
349
- save_btn.click(
350
- fn=save_story,
351
- inputs=[chatbot],
352
- outputs=story_output,
353
- queue=False # Process immediately
354
- )
355
 
356
- # Initialize buttons with default fantasy genre examples
357
- initial_examples = get_examples_for_genre("fantasy")
358
- initial_button_data = tuple(
359
- initial_examples[i] if i < len(initial_examples) else ""
360
- for i in range(4)
361
- )
362
-
363
- # Update button text on page load
364
- demo.load(
365
- fn=lambda: initial_button_data,
366
- outputs=starter_buttons,
367
- queue=False
368
- )
369
 
370
- # Run the app
371
  if __name__ == "__main__":
372
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
1
  import gradio as gr
2
  import os
3
+ from huggingface_hub import InferenceClient
4
  import random
5
+ from typing import List, Tuple
 
 
 
 
 
 
 
 
6
 
7
  # Get token from environment variable
8
  hf_token = os.environ.get("HF_TOKEN")
9
  client = InferenceClient("HuggingFaceH4/zephyr-7b-beta", token=hf_token)
10
 
11
+ # Story genres and starter prompts
12
  GENRE_EXAMPLES = {
13
+ "Fantasy": [
14
+ "You enter the ancient forest seeking the wizard's tower.",
15
+ "Approaching the dragon cautiously, you raise your shield."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  ],
17
+ "Sci-Fi": [
18
+ "Hacking into the space station's mainframe, you uncover a secret.",
19
+ "Investigating the strange signal from the abandoned planet, you find more than you expected."
 
 
20
  ],
21
+ "Mystery": [
22
+ "Examining the crime scene, you notice an overlooked clue.",
23
+ "Following the suspicious figure through foggy streets leads you to a discovery."
 
 
24
  ],
25
+ "Horror": [
26
+ "You slowly open the creaking door to the basement.",
27
+ "Reading the forbidden text, the candles flicker ominously."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  ]
29
  }
30
 
31
+ # System prompt for generating stories
32
+ def get_story_prompt(genre: str) -> str:
33
+ return f"""You are an AI storyteller crafting an interactive {genre} adventure.
34
+ Write engaging scenes with vivid details and always end each response with three numbered choices.
35
+ Follow the format:
36
+
37
+ 1. [Complete action-based choice]
38
+ 2. [Complete action-based choice]
39
+ 3. [Complete action-based choice]"""
40
+
41
+ # Generate initial story
42
+ def generate_story_intro(genre: str) -> str:
43
+ prompt = get_story_prompt(genre)
44
+ example = random.choice(GENRE_EXAMPLES[genre])
45
+ response = client.text_generation(prompt=f"{prompt}\n\nUser: {example}\nAssistant:", max_new_tokens=300)
46
+ return response[0]['generated_text'].strip()
47
+
48
+ # Continue the story
49
+ def continue_story(user_choice: str, history: List[Tuple[str, str]], genre: str) -> str:
50
+ prompt = get_story_prompt(genre)
51
+ conversation = "\n".join([f"User: {turn[0]}\nAssistant: {turn[1]}" for turn in history[-5:]])
52
+ full_prompt = f"{prompt}\n\n{conversation}\nUser: {user_choice}\nAssistant:"
53
+ response = client.text_generation(prompt=full_prompt, max_new_tokens=300)
54
+ return response[0]['generated_text'].strip()
55
+
56
+ # Reset the conversation
57
+ def reset_story() -> Tuple[List[Tuple[str, str]], str, str]:
58
+ return [], "", ""
59
+
60
+ # Gradio UI Setup
61
+ with gr.Blocks() as demo:
62
+ gr.Markdown(
63
+ """
64
+ # 🌟 AI Story Studio
65
+ **Create Your Own Adventure with AI!**
66
+ Choose a genre, start your journey, and guide the story with your choices.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
68
+ ## 🕹️ How to Play:
69
+ 1. **Pick a genre** from the dropdown.
70
+ 2. **Start with a story prompt** or enter your own beginning.
71
+ 3. **Choose from the AI's options** or type your own response to shape the adventure!
 
 
 
 
 
 
 
 
 
72
 
73
+ *Tip: Your choices affect the story's outcome!*
74
+ """
75
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  with gr.Row():
78
+ genre_dropdown = gr.Dropdown(
79
+ choices=list(GENRE_EXAMPLES.keys()), label="Select Genre", value="Fantasy"
80
+ )
81
+ start_story_btn = gr.Button("Start New Story")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
82
 
83
+ chat_history = gr.Chatbot(height=400)
84
+ user_input = gr.Textbox(placeholder="Type your next move...")
 
 
 
 
85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  with gr.Row():
87
+ submit_btn = gr.Button("Continue Story", variant="primary")
88
+ clear_btn = gr.Button("Reset")
 
 
 
 
 
 
 
 
 
 
 
 
 
89
 
90
+ # Function connections
91
+ start_story_btn.click(fn=generate_story_intro, inputs=[genre_dropdown], outputs=chat_history)
92
+ submit_btn.click(fn=continue_story, inputs=[user_input, chat_history, genre_dropdown], outputs=chat_history)
93
+ clear_btn.click(fn=reset_story, inputs=[], outputs=[chat_history, user_input])
 
 
 
 
 
 
 
 
 
94
 
95
+ # Launch the app
96
  if __name__ == "__main__":
97
+ demo.launch()