rui3000 commited on
Commit
bb72572
·
verified ·
1 Parent(s): 1f249ee

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +114 -171
app.py CHANGED
@@ -3,25 +3,20 @@ import uuid
3
  import httpx
4
  import os
5
 
 
 
 
 
 
 
 
 
6
  # FastAPI imports
7
  from fastapi import FastAPI, Request, Body
8
  from fastapi.responses import RedirectResponse
9
  from RockPaperScissor.routes import game_router
10
  from RockPaperScissor.services.service_instance import game_service
11
 
12
- # Import the LLM service and GPU function
13
- from RockPaperScissor.services.LLM_service import LLMService, generate_text_with_gpu, llm_service_instance
14
-
15
- # Import spaces after the GPU function is defined
16
- try:
17
- import spaces
18
- from spaces import GPU
19
- print("[APP] spaces.GPU imported successfully")
20
- except ImportError:
21
- print("[APP] spaces.GPU not available")
22
- def GPU(f):
23
- return f
24
-
25
  # --- Game Constants and Types ---
26
  from enum import Enum
27
 
@@ -46,6 +41,7 @@ class RockPaperScissorsUI:
46
  "adaptive_markov": "Adaptive Markov AI: Uses entropy-weighted Markov and frequency models to predict your next move."
47
  }
48
  self.last_move = None
 
49
 
50
  async def reset_session(self, session_id: str):
51
  # Use environment variable for base URL, fallback to localhost
@@ -60,6 +56,76 @@ class RockPaperScissorsUI:
60
  await self.game_service.clear_session(session_id)
61
  return {"status": "ok"}
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  def create_interface(self):
64
  with gr.Blocks(theme=gr.themes.Soft(), title="Rock Paper Scissors 🎮") as demo:
65
  gr.Markdown("# 🪨📄✂️ Rock Paper Scissors")
@@ -78,19 +144,19 @@ class RockPaperScissorsUI:
78
  rock_btn = gr.Button("🪨 Rock", variant="secondary", elem_classes=["move-btn"])
79
  paper_btn = gr.Button("📄 Paper", variant="secondary", elem_classes=["move-btn"])
80
  scissors_btn = gr.Button("✂️ Scissors", variant="secondary", elem_classes=["move-btn"])
 
81
  # Add End Game button
82
  end_btn = gr.Button("End Game", variant="stop", elem_id="end-game-btn")
83
  # Add Help button
84
- help_btn = gr.Button("💡 Get Help", variant="primary", elem_id="help-btn")
85
  # Add a dedicated box for the help answer, initially empty
86
- help_answer_box = gr.Markdown("", visible=True)
87
 
88
  with gr.Column(scale=2):
89
  gr.Markdown("### 📊 Game Statistics")
90
- stats_display = gr.Markdown()
91
  result_display = gr.Markdown("Make your move!")
92
  end_result_display = gr.Markdown(visible=False)
93
- status_display = gr.Markdown(visible=True)
94
 
95
  ai_dropdown.change(
96
  fn=self.update_ai_description,
@@ -99,7 +165,6 @@ class RockPaperScissorsUI:
99
  )
100
 
101
  # Use a Gradio State to store the session ID
102
- move_state = gr.State("")
103
  session_id_state = gr.State("")
104
 
105
  async def play_rock(ai_type, session_id):
@@ -108,12 +173,14 @@ class RockPaperScissorsUI:
108
  self.session_id = session_id
109
  stats, result = await self.play_round(ai_type, "rock")
110
  return stats, result, session_id
 
111
  async def play_paper(ai_type, session_id):
112
  if not session_id:
113
  session_id = f"session_{uuid.uuid4()}"
114
  self.session_id = session_id
115
  stats, result = await self.play_round(ai_type, "paper")
116
  return stats, result, session_id
 
117
  async def play_scissors(ai_type, session_id):
118
  if not session_id:
119
  session_id = f"session_{uuid.uuid4()}"
@@ -121,195 +188,71 @@ class RockPaperScissorsUI:
121
  stats, result = await self.play_round(ai_type, "scissors")
122
  return stats, result, session_id
123
 
124
- # Add a hidden HTML block with JS to auto-save on tab close
125
- gr.HTML("""
126
- <script>
127
- // Store session ID in localStorage whenever it changes
128
- window.setRpsSessionId = function(session_id) {
129
- if (session_id) {
130
- localStorage.setItem('rps_session_id', session_id);
131
- }
132
- };
133
- // On tab close, send session end to backend
134
- window.onbeforeunload = function() {
135
- let session_id = localStorage.getItem('rps_session_id');
136
- if (session_id) {
137
- navigator.sendBeacon("/game/end", JSON.stringify({session_id: session_id}));
138
- }
139
- };
140
- </script>
141
- """)
142
-
143
- # Inject JS to click End Game button on tab close
144
- gr.HTML("""
145
- <script>
146
- window.onbeforeunload = function() {
147
- var btn = document.getElementById('end-game-btn');
148
- if (btn) {
149
- btn.click();
150
- }
151
- };
152
- </script>
153
- """)
154
-
155
- # After each move, update localStorage with the session ID
156
- def update_session_id_js(session_id):
157
- return f"window.setRpsSessionId('{session_id}');"
158
-
159
  rock_btn.click(
160
  fn=play_rock,
161
  inputs=[ai_dropdown, session_id_state],
162
- outputs=[stats_display, result_display, session_id_state],
163
- js=update_session_id_js
164
  )
165
  paper_btn.click(
166
  fn=play_paper,
167
  inputs=[ai_dropdown, session_id_state],
168
- outputs=[stats_display, result_display, session_id_state],
169
- js=update_session_id_js
170
  )
171
  scissors_btn.click(
172
  fn=play_scissors,
173
  inputs=[ai_dropdown, session_id_state],
174
- outputs=[stats_display, result_display, session_id_state],
175
- js=update_session_id_js
176
  )
177
 
178
  # End Game button logic
179
  async def end_game(session_id):
180
  if not session_id:
181
  return "No session to end. Play a round first!"
182
- result = await self.reset_session(session_id)
183
- return f"End Game: {result['status']} - {result.get('message', '')}"
 
 
 
184
 
185
  end_btn.click(
186
  fn=end_game,
187
  inputs=[session_id_state],
188
- outputs=[end_result_display],
189
  )
190
- end_result_display.visible = True
191
-
192
  # Help button logic
193
- async def get_help(session_id):
194
- # Show loading message while waiting
195
- loading_message = "Generating answer, please wait..."
196
- try:
197
- base_url = os.getenv("HF_SPACE_URL", "http://localhost:7860")
198
- async with httpx.AsyncClient() as client:
199
- response = await client.post(f"{base_url}/api/help", json={"session_id": session_id})
200
- if response.status_code == 200:
201
- data = response.json()
202
- suggestion = data.get("suggestion", "No suggestion available.")
203
- else:
204
- suggestion = "Sorry, I'm having trouble getting help right now."
205
- except Exception as e:
206
- suggestion = f"Error getting help: {str(e)}"
207
- return suggestion
208
-
209
- # When help button is clicked, first show loading, then update with answer
210
- def show_loading():
211
- return "Generating answer, please wait..."
212
-
213
- help_btn.click(
214
- fn=show_loading,
215
- inputs=[],
216
- outputs=[help_answer_box],
217
- queue=False
218
- )
219
  help_btn.click(
220
- fn=get_help,
221
  inputs=[session_id_state],
222
- outputs=[help_answer_box],
223
- queue=True
224
  )
225
 
226
- return demo
227
-
228
- def update_ai_description(self, ai_type: str) -> str:
229
- return self.ai_descriptions[ai_type]
230
-
231
- async def play_round(self, ai_type: str, move: str):
232
- if not self.session_id:
233
- return "Internal Error: Session ID missing in play_round.", "Error"
234
- result = await self.game_service.play_round(self.session_id, move, ai_type)
235
- stats = result["stats"]
236
- stats_text = f"""
237
- ### Game Statistics
238
- - Total Rounds: {stats['total_rounds']}
239
- - Player Wins: {stats['player_wins']} ({stats['player_win_rate']})
240
- - AI Wins: {stats['ai_wins']} ({stats['ai_win_rate']})
241
- - Draws: {stats['draws']}
242
-
243
- ### Player Move Distribution
244
- - Rock: {stats['player_moves']['rock']}
245
- - Paper: {stats['player_moves']['paper']}
246
- - Scissors: {stats['player_moves']['scissors']}
247
-
248
- ### AI Move Distribution
249
- - Rock: {stats['ai_moves']['rock']}
250
- - Paper: {stats['ai_moves']['paper']}
251
- - Scissors: {stats['ai_moves']['scissors']}
252
- """
253
- result_text = f"""
254
- ### Round Result
255
- You played: {result['player_move'].upper()}
256
- AI played: {result['ai_move'].upper()}
257
- Result: {result['result'].replace('_', ' ').title()}
258
- """
259
- return stats_text, result_text
260
 
261
- async def clear_session(self, session_id: str):
262
- await self.game_service.clear_session(session_id)
263
- return {"status": "ok"}
264
 
265
- # Create FastAPI app
266
  app = FastAPI()
267
 
268
- # Add help endpoint
269
- @app.post("/api/help")
270
- async def get_help(request: Request, body: dict = Body(...)):
271
- try:
272
- session_id = body.get("session_id")
273
- stats = None
274
- if session_id:
275
- # Try to get stats from the game service
276
- session_data = game_service.cache.get_session(session_id)
277
- if session_data:
278
- # Calculate AI move distribution
279
- ai_moves = [round['ai_move'] for round in session_data['rounds']]
280
- total_moves = len(ai_moves)
281
- if total_moves > 0:
282
- move_distribution = {
283
- "rock": int((ai_moves.count("rock") / total_moves) * 100),
284
- "paper": int((ai_moves.count("paper") / total_moves) * 100),
285
- "scissors": int((ai_moves.count("scissors") / total_moves) * 100)
286
- }
287
- stats = {"move_distribution": move_distribution}
288
- suggestion = await llm_service_instance.generate_response("help", stats=stats)
289
- print("Help suggestion:", suggestion) # Debug print
290
- return {"suggestion": suggestion}
291
- except Exception as e:
292
- return {"error": str(e), "suggestion": "Sorry, I'm having trouble generating a suggestion right now."}
293
 
294
- # Create Gradio interface
295
  ui = RockPaperScissorsUI(game_service)
296
  demo = ui.create_interface()
297
 
298
- # Mount Gradio app
299
- app.include_router(game_router, prefix="/api/game")
300
- app = gr.mount_gradio_app(app, demo, path="/")
301
-
302
- # Initialize the app
303
- @app.on_event("startup")
304
- async def startup_event():
305
- await game_service.initialize()
306
- print("[APP] Startup complete - LLM will initialize on first use")
307
- print(f"[APP] GPU function available: {generate_text_with_gpu}")
308
 
309
- @app.on_event("shutdown")
310
- async def shutdown_event():
311
- await llm_service_instance.close()
312
 
313
  if __name__ == "__main__":
 
314
  import uvicorn
315
- uvicorn.run("app:app", host="0.0.0.0", port=7860, reload=False)
 
3
  import httpx
4
  import os
5
 
6
+ # Import spaces early
7
+ import spaces
8
+ print("[APP] Spaces imported successfully")
9
+
10
+ # Import the LLM service and GPU function EARLY to ensure detection
11
+ from RockPaperScissor.services.LLM_service import LLMService, generate_text_with_gpu, llm_service_instance
12
+ print(f"[APP] LLM GPU function imported: {generate_text_with_gpu.__name__}")
13
+
14
  # FastAPI imports
15
  from fastapi import FastAPI, Request, Body
16
  from fastapi.responses import RedirectResponse
17
  from RockPaperScissor.routes import game_router
18
  from RockPaperScissor.services.service_instance import game_service
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  # --- Game Constants and Types ---
21
  from enum import Enum
22
 
 
41
  "adaptive_markov": "Adaptive Markov AI: Uses entropy-weighted Markov and frequency models to predict your next move."
42
  }
43
  self.last_move = None
44
+ print("[APP] RockPaperScissorsUI initialized")
45
 
46
  async def reset_session(self, session_id: str):
47
  # Use environment variable for base URL, fallback to localhost
 
56
  await self.game_service.clear_session(session_id)
57
  return {"status": "ok"}
58
 
59
+ def update_ai_description(self, ai_type):
60
+ """Update AI description based on selection"""
61
+ return self.ai_descriptions.get(ai_type, "Unknown AI type")
62
+
63
+ async def play_round(self, ai_type: str, player_move: str):
64
+ """Play a round of Rock Paper Scissors"""
65
+ try:
66
+ # Ensure we have a session
67
+ if not self.session_id:
68
+ self.session_id = f"session_{uuid.uuid4()}"
69
+
70
+ # Make the move via the game service
71
+ result = await self.game_service.play_round(
72
+ self.session_id,
73
+ player_move,
74
+ ai_type
75
+ )
76
+
77
+ # Format the result for display
78
+ player_emoji = {"rock": "🪨", "paper": "📄", "scissors": "✂️"}
79
+ ai_emoji = {"rock": "🪨", "paper": "📄", "scissors": "✂️"}
80
+
81
+ result_text = f"""
82
+ ## Last Round
83
+ **You:** {player_emoji[player_move]} {player_move.title()}
84
+ **AI:** {ai_emoji[result['ai_move']]} {result['ai_move'].title()}
85
+ **Result:** {result['result'].replace('_', ' ').title()}
86
+
87
+ ## Game Statistics
88
+ **Rounds Played:** {result['stats']['total_rounds']}
89
+ **Your Wins:** {result['stats']['player_wins']}
90
+ **AI Wins:** {result['stats']['ai_wins']}
91
+ **Draws:** {result['stats']['draws']}
92
+ **Win Rate:** {result['stats']['player_win_rate']:.1f}%
93
+ """
94
+
95
+ stats_text = f"""
96
+ ### AI Move Distribution
97
+ **Rock:** {result['stats']['move_distribution']['rock']} times
98
+ **Paper:** {result['stats']['move_distribution']['paper']} times
99
+ **Scissors:** {result['stats']['move_distribution']['scissors']} times
100
+ """
101
+
102
+ self.last_move = player_move
103
+ return stats_text, result_text
104
+
105
+ except Exception as e:
106
+ print(f"[APP] Error in play_round: {e}")
107
+ return f"Error: {str(e)}", "Please try again"
108
+
109
+ async def get_ai_help(self, session_id: str):
110
+ """Get strategic advice from the AI"""
111
+ try:
112
+ if not session_id:
113
+ return "Play a few rounds first to get personalized advice!"
114
+
115
+ # Get current game stats
116
+ stats = await self.game_service.get_session_stats(session_id)
117
+
118
+ if not stats or stats.get('total_rounds', 0) == 0:
119
+ return "Play a few rounds first to get personalized advice!"
120
+
121
+ # Use the LLM service for strategic advice
122
+ advice = await llm_service_instance.generate_response("", stats)
123
+ return advice
124
+
125
+ except Exception as e:
126
+ print(f"[APP] Error getting AI help: {e}")
127
+ return ">>> Try to observe patterns in the AI's moves and counter them strategically!\nRecommendation: Rock"
128
+
129
  def create_interface(self):
130
  with gr.Blocks(theme=gr.themes.Soft(), title="Rock Paper Scissors 🎮") as demo:
131
  gr.Markdown("# 🪨📄✂️ Rock Paper Scissors")
 
144
  rock_btn = gr.Button("🪨 Rock", variant="secondary", elem_classes=["move-btn"])
145
  paper_btn = gr.Button("📄 Paper", variant="secondary", elem_classes=["move-btn"])
146
  scissors_btn = gr.Button("✂️ Scissors", variant="secondary", elem_classes=["move-btn"])
147
+
148
  # Add End Game button
149
  end_btn = gr.Button("End Game", variant="stop", elem_id="end-game-btn")
150
  # Add Help button
151
+ help_btn = gr.Button("💡 Get AI Help", variant="primary", elem_id="help-btn")
152
  # Add a dedicated box for the help answer, initially empty
153
+ help_answer_box = gr.Markdown("Click 'Get AI Help' for strategic advice!", visible=True)
154
 
155
  with gr.Column(scale=2):
156
  gr.Markdown("### 📊 Game Statistics")
157
+ stats_display = gr.Markdown("Play a round to see statistics!")
158
  result_display = gr.Markdown("Make your move!")
159
  end_result_display = gr.Markdown(visible=False)
 
160
 
161
  ai_dropdown.change(
162
  fn=self.update_ai_description,
 
165
  )
166
 
167
  # Use a Gradio State to store the session ID
 
168
  session_id_state = gr.State("")
169
 
170
  async def play_rock(ai_type, session_id):
 
173
  self.session_id = session_id
174
  stats, result = await self.play_round(ai_type, "rock")
175
  return stats, result, session_id
176
+
177
  async def play_paper(ai_type, session_id):
178
  if not session_id:
179
  session_id = f"session_{uuid.uuid4()}"
180
  self.session_id = session_id
181
  stats, result = await self.play_round(ai_type, "paper")
182
  return stats, result, session_id
183
+
184
  async def play_scissors(ai_type, session_id):
185
  if not session_id:
186
  session_id = f"session_{uuid.uuid4()}"
 
188
  stats, result = await self.play_round(ai_type, "scissors")
189
  return stats, result, session_id
190
 
191
+ # Button click handlers
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  rock_btn.click(
193
  fn=play_rock,
194
  inputs=[ai_dropdown, session_id_state],
195
+ outputs=[stats_display, result_display, session_id_state]
 
196
  )
197
  paper_btn.click(
198
  fn=play_paper,
199
  inputs=[ai_dropdown, session_id_state],
200
+ outputs=[stats_display, result_display, session_id_state]
 
201
  )
202
  scissors_btn.click(
203
  fn=play_scissors,
204
  inputs=[ai_dropdown, session_id_state],
205
+ outputs=[stats_display, result_display, session_id_state]
 
206
  )
207
 
208
  # End Game button logic
209
  async def end_game(session_id):
210
  if not session_id:
211
  return "No session to end. Play a round first!"
212
+ try:
213
+ result = await self.reset_session(session_id)
214
+ return f"Game ended successfully! Start a new game by making a move."
215
+ except Exception as e:
216
+ return f"Error ending game: {str(e)}"
217
 
218
  end_btn.click(
219
  fn=end_game,
220
  inputs=[session_id_state],
221
+ outputs=[end_result_display]
222
  )
223
+
 
224
  # Help button logic
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
225
  help_btn.click(
226
+ fn=self.get_ai_help,
227
  inputs=[session_id_state],
228
+ outputs=[help_answer_box]
 
229
  )
230
 
231
+ end_result_display.visible = True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
+ return demo
 
 
234
 
235
+ # Create the FastAPI app
236
  app = FastAPI()
237
 
238
+ # Add the game routes
239
+ app.include_router(game_router, prefix="/api")
240
+
241
+ @app.get("/")
242
+ async def root():
243
+ return RedirectResponse(url="/gradio")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
244
 
245
+ # Create the UI
246
  ui = RockPaperScissorsUI(game_service)
247
  demo = ui.create_interface()
248
 
249
+ # Mount Gradio on FastAPI
250
+ app = gr.mount_gradio_app(app, demo, path="/gradio")
 
 
 
 
 
 
 
 
251
 
252
+ print("[APP] Application setup completed successfully")
253
+ print(f"[APP] GPU function status: {generate_text_with_gpu.__name__} ready")
 
254
 
255
  if __name__ == "__main__":
256
+ print("[APP] Starting application...")
257
  import uvicorn
258
+ uvicorn.run(app, host="0.0.0.0", port=7860)