openfree commited on
Commit
02ac8b3
Β·
verified Β·
1 Parent(s): 5679cde

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -121
app.py CHANGED
@@ -18,7 +18,7 @@ if 'OPENAI_API_KEY' not in os.environ:
18
  pxt.drop_dir('ai_rpg', force=True)
19
  pxt.create_dir('ai_rpg')
20
 
21
- # Regular function (not UDF)
22
  def initialize_stats(genre: str) -> str:
23
  """Initialize player stats based on the selected genre"""
24
  base_stats = {
@@ -37,6 +37,34 @@ def initialize_stats(genre: str) -> str:
37
  # Default stats if genre not found
38
  return "Health: 100, Energy: 100, Strength: 7, Intelligence: 7, Agility: 7, Money: 100"
39
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
  @pxt.udf
41
  def generate_messages(genre: str, player_name: str, initial_scenario: str, player_input: str, turn_number: int, stats: str) -> list[dict]:
42
  return [
@@ -74,30 +102,6 @@ def generate_messages(genre: str, player_name: str, initial_scenario: str, playe
74
  }
75
  ]
76
 
77
- @pxt.udf
78
- def fallback_response(player_input: str, turn_number: int) -> str:
79
- """Generate a fallback response when API fails"""
80
- if turn_number == 0:
81
- return """πŸ“œ **STORY**: Your adventure begins as you assess your surroundings. The world around you seems full of possibilities and dangers alike. What will you do next?
82
-
83
- πŸ“Š **STATS UPDATE**: Your current stats remain unchanged.
84
-
85
- 🎯 **OPTIONS**:
86
- 1. Explore the area carefully
87
- 2. Look for signs of civilization
88
- 3. Check your inventory
89
- 4. Rest and gather your thoughts"""
90
- else:
91
- return f"""πŸ“œ **STORY**: You decide to {player_input}. As you do, you notice new details about your environment and feel yourself making progress in your quest.
92
-
93
- πŸ“Š **STATS UPDATE**: Your actions have slightly improved your experience.
94
-
95
- 🎯 **OPTIONS**:
96
- 1. Continue on your current path
97
- 2. Try a different approach
98
- 3. Investigate something suspicious
99
- 4. Take a moment to strategize"""
100
-
101
  @pxt.udf
102
  def get_story(response: str) -> str:
103
  """Extract just the story part from the response"""
@@ -156,7 +160,11 @@ interactions = pxt.create_table(
156
  'player_input': pxt.String,
157
  'timestamp': pxt.Timestamp,
158
  'player_stats': pxt.String,
159
- 'random_event': pxt.String
 
 
 
 
160
  }
161
  )
162
 
@@ -170,10 +178,10 @@ interactions.add_computed_column(messages=generate_messages(
170
  interactions.player_stats
171
  ))
172
 
173
- # Changed from gpt-4.1-mini to gpt-3.5-turbo for better compatibility
174
  interactions.add_computed_column(ai_response=openai.chat_completions(
175
  messages=interactions.messages,
176
- model='gpt-3.5-turbo', # Changed to a more widely available model
177
  max_tokens=800,
178
  temperature=0.8
179
  ))
@@ -189,7 +197,7 @@ class RPGGame:
189
  self.turn_number = 0
190
  self.current_stats = ""
191
 
192
- def start_game(self, player_name: str, genre: str, scenario: str) -> tuple[str, str, str, list[str]]:
193
  session_id = f"session_{datetime.now().strftime('%Y%m%d%H%M%S')}_{player_name}"
194
  self.current_session_id = session_id
195
  self.turn_number = 0
@@ -198,7 +206,11 @@ class RPGGame:
198
  initial_stats = initialize_stats(genre)
199
  self.current_stats = initial_stats
200
 
 
 
 
201
  try:
 
202
  interactions.insert([{
203
  'session_id': session_id,
204
  'player_name': player_name,
@@ -207,39 +219,37 @@ class RPGGame:
207
  'turn_number': 0,
208
  'player_input': "Game starts",
209
  'timestamp': datetime.now(),
210
- 'player_stats': initial_stats, # Store string result
211
- 'random_event': ""
 
 
 
 
212
  }])
213
 
214
- # Added retry logic for API calls
215
- max_retries = 3
216
- for attempt in range(max_retries):
217
- try:
218
- result = interactions.select(
219
- interactions.story_text,
220
- interactions.stats_update,
221
- interactions.options
222
- ).where(
223
- (interactions.session_id == session_id) &
224
- (interactions.turn_number == 0)
225
- ).collect()
226
-
227
- return session_id, result['story_text'][0], result['stats_update'][0], result['options'][0]
228
- except Exception as e:
229
- if attempt < max_retries - 1:
230
- time.sleep(2) # Wait before retrying
231
- else:
232
- # Use fallback response if all retries fail
233
- fallback = fallback_response("Game starts", 0)
234
- return session_id, get_story(fallback), get_stats_update(fallback), get_options(fallback)
235
  except Exception as e:
236
- # Use fallback if insert fails
237
- fallback = fallback_response("Game starts", 0)
238
- return session_id, get_story(fallback), get_stats_update(fallback), get_options(fallback)
239
 
240
  def process_action(self, action: str) -> tuple[str, str, list[str]]:
241
  if not self.current_session_id:
242
- return "No active game session. Please start a new game.", "No stats", []
243
 
244
  self.turn_number += 1
245
 
@@ -273,6 +283,9 @@ class RPGGame:
273
  if random_event_val:
274
  action = f"{action} ({random_event_val})"
275
 
 
 
 
276
  interactions.insert([{
277
  'session_id': self.current_session_id,
278
  'player_name': prev_turn['player_name'][0],
@@ -282,37 +295,35 @@ class RPGGame:
282
  'player_input': action,
283
  'timestamp': datetime.now(),
284
  'player_stats': self.current_stats,
285
- 'random_event': random_event_val
 
 
 
 
286
  }])
287
 
288
- # Added retry logic for API calls
289
- max_retries = 3
290
- for attempt in range(max_retries):
291
- try:
292
- result = interactions.select(
293
- interactions.story_text,
294
- interactions.stats_update,
295
- interactions.options
296
- ).where(
297
- (interactions.session_id == self.current_session_id) &
298
- (interactions.turn_number == self.turn_number)
299
- ).collect()
300
-
301
- # Update stats for next turn
302
- self.current_stats = result['stats_update'][0]
303
-
304
- return result['story_text'][0], result['stats_update'][0], result['options'][0]
305
- except Exception as e:
306
- if attempt < max_retries - 1:
307
- time.sleep(2) # Wait before retrying
308
- else:
309
- # Use fallback response if all retries fail
310
- fallback = fallback_response(action, self.turn_number)
311
- return get_story(fallback), get_stats_update(fallback), get_options(fallback)
312
  except Exception as e:
313
- # Use fallback if something fails
314
- fallback = fallback_response(action, self.turn_number)
315
- return get_story(fallback), get_stats_update(fallback), get_options(fallback)
316
 
317
  def create_interface():
318
  game = RPGGame()
@@ -500,33 +511,39 @@ def create_interface():
500
  )
501
 
502
  try:
503
- _, initial_story, initial_stats, initial_options = game.start_game(name, genre_choice, scenario_text)
504
-
505
- history_df = interactions.select(
506
- turn=interactions.turn_number,
507
- action=interactions.player_input,
508
- response=interactions.story_text
509
- ).where(
510
- interactions.session_id == game.current_session_id
511
- ).order_by(
512
- interactions.turn_number
513
- ).collect().to_pandas()
514
 
515
- history_data = [
516
- [str(row['turn']), row['action'], row['response']]
517
- for _, row in history_df.iterrows()
518
- ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
519
 
520
  story_html = f"<div class='story-container'>{initial_story}</div>"
521
  stats_html = f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{initial_stats}</div>"
522
 
523
  return story_html, stats_html, gr.Radio(choices=initial_options, interactive=True), history_data
524
  except Exception as e:
 
 
525
  return (
526
- f"<div class='story-container'>Game start error: {str(e)}</div>",
527
- "<div class='stats-container'>No stat information</div>",
528
- [],
529
- []
530
  )
531
 
532
  def process_player_action(action_choice):
@@ -541,31 +558,38 @@ def create_interface():
541
 
542
  story, stats, options = game.process_action(action_choice)
543
 
544
- history_df = interactions.select(
545
- turn=interactions.turn_number,
546
- action=interactions.player_input,
547
- response=interactions.story_text
548
- ).where(
549
- interactions.session_id == game.current_session_id
550
- ).order_by(
551
- interactions.turn_number
552
- ).collect().to_pandas()
553
-
554
- history_data = [
555
- [str(row['turn']), row['action'], row['response']]
556
- for _, row in history_df.iterrows()
557
- ]
 
 
 
 
 
558
 
559
  story_html = f"<div class='story-container'>{story}</div>"
560
  stats_html = f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{stats}</div>"
561
 
562
  return story_html, stats_html, gr.Radio(choices=options, interactive=True), history_data
563
  except Exception as e:
 
 
564
  return (
565
- f"<div class='story-container'>Error: {str(e)}</div>",
566
- "<div class='stats-container'>No stat information</div>",
567
- [],
568
- []
569
  )
570
 
571
  start_button.click(
 
18
  pxt.drop_dir('ai_rpg', force=True)
19
  pxt.create_dir('ai_rpg')
20
 
21
+ # Regular function (not UDF) for initializing stats
22
  def initialize_stats(genre: str) -> str:
23
  """Initialize player stats based on the selected genre"""
24
  base_stats = {
 
37
  # Default stats if genre not found
38
  return "Health: 100, Energy: 100, Strength: 7, Intelligence: 7, Agility: 7, Money: 100"
39
 
40
+ # Regular Python function for fallback responses (not a UDF)
41
+ def create_fallback_response(player_input: str, turn_number: int) -> dict:
42
+ """Generate a fallback response when API fails"""
43
+ if turn_number == 0:
44
+ story = "Your adventure begins as you assess your surroundings. The world around you seems full of possibilities and dangers alike. What will you do next?"
45
+ stats = "Your current stats remain unchanged."
46
+ options = [
47
+ "Explore the area carefully",
48
+ "Look for signs of civilization",
49
+ "Check your inventory",
50
+ "Rest and gather your thoughts"
51
+ ]
52
+ else:
53
+ story = f"You decide to {player_input}. As you do, you notice new details about your environment and feel yourself making progress in your quest."
54
+ stats = "Your actions have slightly improved your experience."
55
+ options = [
56
+ "Continue on your current path",
57
+ "Try a different approach",
58
+ "Investigate something suspicious",
59
+ "Take a moment to strategize"
60
+ ]
61
+
62
+ return {
63
+ "story": story,
64
+ "stats": stats,
65
+ "options": options
66
+ }
67
+
68
  @pxt.udf
69
  def generate_messages(genre: str, player_name: str, initial_scenario: str, player_input: str, turn_number: int, stats: str) -> list[dict]:
70
  return [
 
102
  }
103
  ]
104
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
  @pxt.udf
106
  def get_story(response: str) -> str:
107
  """Extract just the story part from the response"""
 
160
  'player_input': pxt.String,
161
  'timestamp': pxt.Timestamp,
162
  'player_stats': pxt.String,
163
+ 'random_event': pxt.String,
164
+ 'use_fallback': pxt.Boolean,
165
+ 'fallback_story': pxt.String,
166
+ 'fallback_stats': pxt.String,
167
+ 'fallback_options': pxt.String
168
  }
169
  )
170
 
 
178
  interactions.player_stats
179
  ))
180
 
181
+ # Changed to gpt-3.5-turbo for better compatibility
182
  interactions.add_computed_column(ai_response=openai.chat_completions(
183
  messages=interactions.messages,
184
+ model='gpt-3.5-turbo',
185
  max_tokens=800,
186
  temperature=0.8
187
  ))
 
197
  self.turn_number = 0
198
  self.current_stats = ""
199
 
200
+ def start_game(self, player_name: str, genre: str, scenario: str) -> tuple[str, str, list[str]]:
201
  session_id = f"session_{datetime.now().strftime('%Y%m%d%H%M%S')}_{player_name}"
202
  self.current_session_id = session_id
203
  self.turn_number = 0
 
206
  initial_stats = initialize_stats(genre)
207
  self.current_stats = initial_stats
208
 
209
+ # Create fallback content just in case
210
+ fallback = create_fallback_response("Game starts", 0)
211
+
212
  try:
213
+ # First try without fallback
214
  interactions.insert([{
215
  'session_id': session_id,
216
  'player_name': player_name,
 
219
  'turn_number': 0,
220
  'player_input': "Game starts",
221
  'timestamp': datetime.now(),
222
+ 'player_stats': initial_stats,
223
+ 'random_event': "",
224
+ 'use_fallback': False,
225
+ 'fallback_story': fallback["story"],
226
+ 'fallback_stats': fallback["stats"],
227
+ 'fallback_options': ",".join(fallback["options"])
228
  }])
229
 
230
+ # Try to get response
231
+ try:
232
+ result = interactions.select(
233
+ interactions.story_text,
234
+ interactions.stats_update,
235
+ interactions.options
236
+ ).where(
237
+ (interactions.session_id == session_id) &
238
+ (interactions.turn_number == 0)
239
+ ).collect()
240
+
241
+ return result['story_text'][0], result['stats_update'][0], result['options'][0]
242
+ except Exception as e:
243
+ # If OpenAI fails, use the fallback values
244
+ return fallback["story"], fallback["stats"], fallback["options"]
245
+
 
 
 
 
 
246
  except Exception as e:
247
+ # If everything fails, return fallback
248
+ return fallback["story"], fallback["stats"], fallback["options"]
 
249
 
250
  def process_action(self, action: str) -> tuple[str, str, list[str]]:
251
  if not self.current_session_id:
252
+ return "No active game session. Please start a new game.", "No stats", ["Start a new game"]
253
 
254
  self.turn_number += 1
255
 
 
283
  if random_event_val:
284
  action = f"{action} ({random_event_val})"
285
 
286
+ # Create fallback content
287
+ fallback = create_fallback_response(action, self.turn_number)
288
+
289
  interactions.insert([{
290
  'session_id': self.current_session_id,
291
  'player_name': prev_turn['player_name'][0],
 
295
  'player_input': action,
296
  'timestamp': datetime.now(),
297
  'player_stats': self.current_stats,
298
+ 'random_event': random_event_val,
299
+ 'use_fallback': False,
300
+ 'fallback_story': fallback["story"],
301
+ 'fallback_stats': fallback["stats"],
302
+ 'fallback_options': ",".join(fallback["options"])
303
  }])
304
 
305
+ try:
306
+ result = interactions.select(
307
+ interactions.story_text,
308
+ interactions.stats_update,
309
+ interactions.options
310
+ ).where(
311
+ (interactions.session_id == self.current_session_id) &
312
+ (interactions.turn_number == self.turn_number)
313
+ ).collect()
314
+
315
+ # Update stats for next turn
316
+ self.current_stats = result['stats_update'][0]
317
+
318
+ return result['story_text'][0], result['stats_update'][0], result['options'][0]
319
+ except Exception as e:
320
+ # If OpenAI fails, use the fallback values
321
+ return fallback["story"], fallback["stats"], fallback["options"]
322
+
 
 
 
 
 
 
323
  except Exception as e:
324
+ # If everything fails, use fallback
325
+ fallback = create_fallback_response(action, self.turn_number)
326
+ return fallback["story"], fallback["stats"], fallback["options"]
327
 
328
  def create_interface():
329
  game = RPGGame()
 
511
  )
512
 
513
  try:
514
+ initial_story, initial_stats, initial_options = game.start_game(name, genre_choice, scenario_text)
 
 
 
 
 
 
 
 
 
 
515
 
516
+ # Try to get history, but if it fails, create empty history
517
+ try:
518
+ history_df = interactions.select(
519
+ turn=interactions.turn_number,
520
+ action=interactions.player_input,
521
+ response=interactions.story_text
522
+ ).where(
523
+ (interactions.session_id == game.current_session_id)
524
+ ).order_by(
525
+ interactions.turn_number
526
+ ).collect().to_pandas()
527
+
528
+ history_data = [
529
+ [str(row['turn']), row['action'], row['response']]
530
+ for _, row in history_df.iterrows()
531
+ ]
532
+ except Exception as e:
533
+ history_data = [["0", "Game starts", initial_story]]
534
 
535
  story_html = f"<div class='story-container'>{initial_story}</div>"
536
  stats_html = f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{initial_stats}</div>"
537
 
538
  return story_html, stats_html, gr.Radio(choices=initial_options, interactive=True), history_data
539
  except Exception as e:
540
+ # Handle any other unexpected errors
541
+ fallback = create_fallback_response("Game starts", 0)
542
  return (
543
+ f"<div class='story-container'>{fallback['story']}</div>",
544
+ f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{fallback['stats']}</div>",
545
+ gr.Radio(choices=fallback['options'], interactive=True),
546
+ [["0", "Game starts", fallback['story']]]
547
  )
548
 
549
  def process_player_action(action_choice):
 
558
 
559
  story, stats, options = game.process_action(action_choice)
560
 
561
+ # Try to get history, but if it fails, create minimal history
562
+ try:
563
+ history_df = interactions.select(
564
+ turn=interactions.turn_number,
565
+ action=interactions.player_input,
566
+ response=interactions.story_text
567
+ ).where(
568
+ (interactions.session_id == game.current_session_id)
569
+ ).order_by(
570
+ interactions.turn_number
571
+ ).collect().to_pandas()
572
+
573
+ history_data = [
574
+ [str(row['turn']), row['action'], row['response']]
575
+ for _, row in history_df.iterrows()
576
+ ]
577
+ except Exception as e:
578
+ # If history retrieval fails, create minimal history
579
+ history_data = [[str(game.turn_number), action_choice, story]]
580
 
581
  story_html = f"<div class='story-container'>{story}</div>"
582
  stats_html = f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{stats}</div>"
583
 
584
  return story_html, stats_html, gr.Radio(choices=options, interactive=True), history_data
585
  except Exception as e:
586
+ # Handle any other unexpected errors
587
+ fallback = create_fallback_response(action_choice, 1)
588
  return (
589
+ f"<div class='story-container'>{fallback['story']}</div>",
590
+ f"<div class='stats-container'><h3>πŸ“Š Character Status</h3>{fallback['stats']}</div>",
591
+ gr.Radio(choices=fallback['options'], interactive=True),
592
+ [[str(game.turn_number), action_choice, fallback['story']]]
593
  )
594
 
595
  start_button.click(