Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import uuid | |
| import httpx | |
| import os | |
| # FastAPI imports | |
| from fastapi import FastAPI, Request, Body | |
| from fastapi.responses import RedirectResponse | |
| from RockPaperScissor.routes import game_router | |
| from RockPaperScissor.services.service_instance import game_service | |
| # Import the LLM service and GPU function | |
| from RockPaperScissor.services.LLM_service import LLMService, generate_text_with_gpu, llm_service_instance | |
| # Import spaces after the GPU function is defined | |
| try: | |
| import spaces | |
| from spaces import GPU | |
| print("[APP] spaces.GPU imported successfully") | |
| except ImportError: | |
| print("[APP] spaces.GPU not available") | |
| def GPU(f): | |
| return f | |
| # --- Game Constants and Types --- | |
| from enum import Enum | |
| class Move(Enum): | |
| ROCK = "rock" | |
| PAPER = "paper" | |
| SCISSORS = "scissors" | |
| class GameResult(Enum): | |
| PLAYER_WIN = "player_win" | |
| AI_WIN = "ai_win" | |
| DRAW = "draw" | |
| # --- Gradio Interface --- | |
| class RockPaperScissorsUI: | |
| def __init__(self, game_service): | |
| self.game_service = game_service | |
| self.session_id = None # Will be set from State | |
| self.ai_models = list(self.game_service.ai_models.keys()) | |
| self.ai_descriptions = { | |
| "random": "Random AI: Makes completely random moves.", | |
| "adaptive_markov": "Adaptive Markov AI: Uses entropy-weighted Markov and frequency models to predict your next move." | |
| } | |
| self.last_move = None | |
| async def reset_session(self, session_id: str): | |
| # Use environment variable for base URL, fallback to localhost | |
| base_url = os.getenv("HF_SPACE_URL", "http://localhost:7860") | |
| async with httpx.AsyncClient() as client: | |
| response = await client.post( | |
| f"{base_url}/api/game/end", | |
| json={"session_id": session_id} | |
| ) | |
| print("[DEBUG] /api/game/end response:", response.text) | |
| # Optionally, also clear the session in-memory (if needed) | |
| await self.game_service.clear_session(session_id) | |
| return {"status": "ok"} | |
| def create_interface(self): | |
| with gr.Blocks(theme=gr.themes.Soft(), title="Rock Paper Scissors ๐ฎ") as demo: | |
| gr.Markdown("# ๐ชจ๐โ๏ธ Rock Paper Scissors") | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown("### ๐ฎ Game Setup") | |
| ai_dropdown = gr.Dropdown( | |
| choices=self.ai_models, | |
| value=self.ai_models[0], | |
| label="Select AI Opponent" | |
| ) | |
| ai_description = gr.Markdown(self.ai_descriptions[self.ai_models[0]]) | |
| with gr.Row(): | |
| rock_btn = gr.Button("๐ชจ Rock", variant="secondary", elem_classes=["move-btn"]) | |
| paper_btn = gr.Button("๐ Paper", variant="secondary", elem_classes=["move-btn"]) | |
| scissors_btn = gr.Button("โ๏ธ Scissors", variant="secondary", elem_classes=["move-btn"]) | |
| # Add End Game button | |
| end_btn = gr.Button("End Game", variant="stop", elem_id="end-game-btn") | |
| # Add Help button | |
| help_btn = gr.Button("๐ก Get Help", variant="primary", elem_id="help-btn") | |
| # Add a dedicated box for the help answer, initially empty | |
| help_answer_box = gr.Markdown("", visible=True) | |
| with gr.Column(scale=2): | |
| gr.Markdown("### ๐ Game Statistics") | |
| stats_display = gr.Markdown() | |
| result_display = gr.Markdown("Make your move!") | |
| end_result_display = gr.Markdown(visible=False) | |
| status_display = gr.Markdown(visible=True) | |
| ai_dropdown.change( | |
| fn=self.update_ai_description, | |
| inputs=[ai_dropdown], | |
| outputs=[ai_description] | |
| ) | |
| # Use a Gradio State to store the session ID | |
| move_state = gr.State("") | |
| session_id_state = gr.State("") | |
| async def play_rock(ai_type, session_id): | |
| if not session_id: | |
| session_id = f"session_{uuid.uuid4()}" | |
| self.session_id = session_id | |
| stats, result = await self.play_round(ai_type, "rock") | |
| return stats, result, session_id | |
| async def play_paper(ai_type, session_id): | |
| if not session_id: | |
| session_id = f"session_{uuid.uuid4()}" | |
| self.session_id = session_id | |
| stats, result = await self.play_round(ai_type, "paper") | |
| return stats, result, session_id | |
| async def play_scissors(ai_type, session_id): | |
| if not session_id: | |
| session_id = f"session_{uuid.uuid4()}" | |
| self.session_id = session_id | |
| stats, result = await self.play_round(ai_type, "scissors") | |
| return stats, result, session_id | |
| # Add a hidden HTML block with JS to auto-save on tab close | |
| gr.HTML(""" | |
| <script> | |
| // Store session ID in localStorage whenever it changes | |
| window.setRpsSessionId = function(session_id) { | |
| if (session_id) { | |
| localStorage.setItem('rps_session_id', session_id); | |
| } | |
| }; | |
| // On tab close, send session end to backend | |
| window.onbeforeunload = function() { | |
| let session_id = localStorage.getItem('rps_session_id'); | |
| if (session_id) { | |
| navigator.sendBeacon("/game/end", JSON.stringify({session_id: session_id})); | |
| } | |
| }; | |
| </script> | |
| """) | |
| # Inject JS to click End Game button on tab close | |
| gr.HTML(""" | |
| <script> | |
| window.onbeforeunload = function() { | |
| var btn = document.getElementById('end-game-btn'); | |
| if (btn) { | |
| btn.click(); | |
| } | |
| }; | |
| </script> | |
| """) | |
| # After each move, update localStorage with the session ID | |
| def update_session_id_js(session_id): | |
| return f"window.setRpsSessionId('{session_id}');" | |
| rock_btn.click( | |
| fn=play_rock, | |
| inputs=[ai_dropdown, session_id_state], | |
| outputs=[stats_display, result_display, session_id_state], | |
| js=update_session_id_js | |
| ) | |
| paper_btn.click( | |
| fn=play_paper, | |
| inputs=[ai_dropdown, session_id_state], | |
| outputs=[stats_display, result_display, session_id_state], | |
| js=update_session_id_js | |
| ) | |
| scissors_btn.click( | |
| fn=play_scissors, | |
| inputs=[ai_dropdown, session_id_state], | |
| outputs=[stats_display, result_display, session_id_state], | |
| js=update_session_id_js | |
| ) | |
| # End Game button logic | |
| async def end_game(session_id): | |
| if not session_id: | |
| return "No session to end. Play a round first!" | |
| result = await self.reset_session(session_id) | |
| return f"End Game: {result['status']} - {result.get('message', '')}" | |
| end_btn.click( | |
| fn=end_game, | |
| inputs=[session_id_state], | |
| outputs=[end_result_display], | |
| ) | |
| end_result_display.visible = True | |
| # Help button logic |