Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,265 +1,257 @@
|
|
| 1 |
# app.py
|
| 2 |
import gradio as gr
|
| 3 |
import asyncio
|
| 4 |
-
import threading
|
| 5 |
import os
|
| 6 |
-
import random
|
|
|
|
| 7 |
|
| 8 |
# Import poke-env components
|
| 9 |
from poke_env.player import Player, RandomPlayer
|
| 10 |
-
from poke_env import AccountConfiguration,ServerConfiguration
|
| 11 |
# Import your custom agent
|
| 12 |
-
from agents import OpenAIAgent
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
# --- Global variables for players and thread ---
|
| 16 |
-
random_player: Player | None = None
|
| 17 |
-
openai_agent: Player | None = None
|
| 18 |
-
agent_init_thread: threading.Thread | None = None
|
| 19 |
-
init_lock = threading.Lock() # To prevent race conditions during init
|
| 20 |
-
agents_initialized = False
|
| 21 |
|
| 22 |
# --- Configuration ---
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
"
|
| 26 |
-
|
|
|
|
|
|
|
|
|
|
| 27 |
|
| 28 |
# --- Dynamic Account Configuration ---
|
| 29 |
-
#
|
| 30 |
-
|
| 31 |
-
OPENAI_AGENT_BASE_NAME = "OpenAIAgent" # You can change this if you like
|
| 32 |
-
|
| 33 |
DEFAULT_BATTLE_FORMAT = "gen9randombattle"
|
| 34 |
|
| 35 |
-
# --- Agent
|
| 36 |
-
def
|
| 37 |
-
"""
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
#
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
print(f"Using OpenAIAgent username: {openai_account_config.username}")
|
| 56 |
-
|
| 57 |
-
# Ensure this runs only once
|
| 58 |
-
with init_lock:
|
| 59 |
-
if agents_initialized:
|
| 60 |
-
print("Agents already initialized.")
|
| 61 |
-
return
|
| 62 |
-
print("Initializing agents...")
|
| 63 |
-
# We need an event loop in this new thread for player initialization
|
| 64 |
-
loop = asyncio.new_event_loop()
|
| 65 |
-
asyncio.set_event_loop(loop)
|
| 66 |
-
try:
|
| 67 |
-
# Initialize Random Player - PASSING THE CONFIGURATION
|
| 68 |
-
# Use the dynamically generated username from random_account_config
|
| 69 |
-
print(f"Initializing RandomPlayer ({random_account_config.username})...")
|
| 70 |
-
random_player = RandomPlayer(
|
| 71 |
-
account_configuration=random_account_config, # <-- Pass the config
|
| 72 |
server_configuration=custom_config,
|
| 73 |
-
battle_format=
|
|
|
|
|
|
|
| 74 |
)
|
| 75 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 76 |
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
print(f"Initializing OpenAIAgent
|
| 80 |
-
|
| 81 |
-
account_configuration=
|
| 82 |
server_configuration=custom_config,
|
| 83 |
-
battle_format=
|
|
|
|
| 84 |
)
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
#
|
| 96 |
-
#
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
#
|
| 112 |
-
|
| 113 |
-
|
|
|
|
|
|
|
| 114 |
"""Sends a challenge using the provided player object."""
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
return "Error:
|
|
|
|
|
|
|
|
|
|
| 119 |
try:
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
#
|
| 124 |
-
await player.send_challenges(
|
| 125 |
-
|
|
|
|
|
|
|
| 126 |
except Exception as e:
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
player_name = getattr(player, 'username', 'unknown player')
|
| 131 |
-
print(f"Error sending challenge from {player_name}:")
|
| 132 |
-
traceback.print_exc() # Print stack trace
|
| 133 |
-
return f"Error sending challenge: {str(e)}. Check console logs."
|
| 134 |
-
|
| 135 |
-
# Wrapper for the async function to use in Gradio, handling agent selection
|
| 136 |
-
def invite_to_battle(agent_choice: str, username: str):
|
| 137 |
-
"""Selects the agent and initiates the battle invitation."""
|
| 138 |
-
global random_player, openai_agent, agents_initialized
|
| 139 |
-
|
| 140 |
-
if not agents_initialized:
|
| 141 |
-
# Try to initialize if the button is clicked before init finishes
|
| 142 |
-
start_agent_initialization()
|
| 143 |
-
return "Agents are initializing, please wait a few seconds and try again."
|
| 144 |
-
|
| 145 |
-
selected_player: Player | None = None
|
| 146 |
-
if agent_choice == "Random Player":
|
| 147 |
-
selected_player = random_player
|
| 148 |
-
if selected_player is None:
|
| 149 |
-
return "Error: Random Player is not available. Initialization might have failed."
|
| 150 |
-
elif agent_choice == "OpenAI Agent":
|
| 151 |
-
selected_player = openai_agent
|
| 152 |
-
if selected_player is None:
|
| 153 |
-
# Check if API key might be the issue
|
| 154 |
-
if not os.getenv("OPENAI_API_KEY"):
|
| 155 |
-
return "Error: OpenAI Agent not available. OPENAI_API_KEY is missing."
|
| 156 |
-
else:
|
| 157 |
-
return "Error: OpenAI Agent not available. Initialization might have failed. Check logs."
|
| 158 |
-
else:
|
| 159 |
-
return "Error: Invalid agent choice selected."
|
| 160 |
|
| 161 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 162 |
username_clean = username.strip()
|
| 163 |
if not username_clean:
|
| 164 |
return "Please enter your Showdown username."
|
|
|
|
|
|
|
| 165 |
|
| 166 |
-
#
|
| 167 |
-
|
| 168 |
-
#
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
if loop.is_running():
|
| 178 |
-
# Schedule the coroutine and wait for its result
|
| 179 |
-
future = asyncio.run_coroutine_threadsafe(
|
| 180 |
-
# Pass the default battle format explicitly if needed
|
| 181 |
-
send_battle_invite_async(selected_player, username_clean, DEFAULT_BATTLE_FORMAT),
|
| 182 |
-
loop
|
| 183 |
-
)
|
| 184 |
-
result = future.result(timeout=30) # Add a timeout
|
| 185 |
-
else:
|
| 186 |
-
result = loop.run_until_complete(
|
| 187 |
-
# Pass the default battle format explicitly if needed
|
| 188 |
-
send_battle_invite_async(selected_player, username_clean, DEFAULT_BATTLE_FORMAT)
|
| 189 |
-
)
|
| 190 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 191 |
return result
|
| 192 |
-
|
| 193 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 194 |
except Exception as e:
|
| 195 |
-
print(f"Unexpected error in invite_to_battle
|
| 196 |
-
import traceback
|
| 197 |
traceback.print_exc()
|
| 198 |
-
return f"An
|
| 199 |
|
| 200 |
|
| 201 |
-
# --- Gradio UI Definition ---
|
| 202 |
-
# iframe code to embed Pokemon Showdown (using
|
| 203 |
iframe_code = """
|
| 204 |
<iframe
|
| 205 |
-
src="https://
|
| 206 |
width="100%"
|
| 207 |
height="800"
|
| 208 |
style="border: none;"
|
| 209 |
referrerpolicy="no-referrer">
|
| 210 |
</iframe>
|
| 211 |
"""
|
|
|
|
|
|
|
| 212 |
|
| 213 |
def main_app():
|
| 214 |
"""Creates and returns the Gradio application interface."""
|
| 215 |
-
#
|
| 216 |
-
start_agent_initialization()
|
| 217 |
|
| 218 |
-
# Using gr.Blocks. The default layout should stretch reasonably wide.
|
| 219 |
with gr.Blocks(title="Pokemon Showdown Agent") as demo:
|
| 220 |
gr.Markdown("# Pokémon Battle Agent")
|
| 221 |
gr.Markdown(
|
| 222 |
"Select an agent, enter **your** Showdown username "
|
| 223 |
-
"(the one you are logged in with below), and click Send Invite."
|
| 224 |
-
"
|
| 225 |
)
|
| 226 |
|
| 227 |
-
# --- Row for Controls at the Top ---
|
| 228 |
with gr.Row():
|
| 229 |
-
# Place controls here
|
| 230 |
agent_dropdown = gr.Dropdown(
|
| 231 |
label="Select Agent",
|
| 232 |
choices=["Random Player", "OpenAI Agent"],
|
| 233 |
value="Random Player",
|
| 234 |
-
scale=1
|
| 235 |
)
|
| 236 |
name_input = gr.Textbox(
|
| 237 |
label="Your Pokémon Showdown Username",
|
| 238 |
placeholder="Enter username used in Showdown below",
|
| 239 |
-
scale=2
|
| 240 |
)
|
| 241 |
-
battle_button = gr.Button("Send Battle Invitation", scale=1)
|
| 242 |
|
|
|
|
|
|
|
| 243 |
|
| 244 |
-
# --- Section for the IFrame below the controls ---
|
| 245 |
gr.Markdown("### Pokémon Showdown Interface")
|
| 246 |
gr.Markdown("Log in/use the username you entered above.")
|
| 247 |
-
# The HTML component containing the iframe will take up the available width
|
| 248 |
gr.HTML(iframe_code)
|
| 249 |
|
| 250 |
-
# --- Connect button click to the invitation function ---
|
| 251 |
battle_button.click(
|
| 252 |
fn=invite_to_battle,
|
| 253 |
inputs=[agent_dropdown, name_input],
|
| 254 |
-
|
| 255 |
)
|
| 256 |
|
| 257 |
return demo
|
| 258 |
|
| 259 |
# --- Main execution block ---
|
| 260 |
if __name__ == "__main__":
|
| 261 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
app = main_app()
|
| 263 |
-
#
|
| 264 |
-
#
|
| 265 |
-
app.launch()
|
|
|
|
| 1 |
# app.py
|
| 2 |
import gradio as gr
|
| 3 |
import asyncio
|
| 4 |
+
import threading # Still needed for asyncio.run in sync context potentially
|
| 5 |
import os
|
| 6 |
+
import random
|
| 7 |
+
import traceback # For detailed error logging
|
| 8 |
|
| 9 |
# Import poke-env components
|
| 10 |
from poke_env.player import Player, RandomPlayer
|
| 11 |
+
from poke_env import AccountConfiguration, ServerConfiguration
|
| 12 |
# Import your custom agent
|
| 13 |
+
from agents import OpenAIAgent # Assuming agents.py exists with OpenAIAgent
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
# --- Configuration ---
|
| 16 |
+
# Use your custom server or the official Smogon server
|
| 17 |
+
# custom_config = ServerConfiguration(
|
| 18 |
+
# "wss://jofthomas.com/showdown/websocket", # WebSocket URL
|
| 19 |
+
# "https://jofthomas.com/showdown/action.php" # Authentication URL
|
| 20 |
+
# )
|
| 21 |
+
# Or use the default Smogon server configuration
|
| 22 |
+
custom_config = ServerConfiguration.get_default() # Easier for general use
|
| 23 |
|
| 24 |
# --- Dynamic Account Configuration ---
|
| 25 |
+
RANDOM_PLAYER_BASE_NAME = "TempRandAgent" # Changed base name slightly
|
| 26 |
+
OPENAI_AGENT_BASE_NAME = "TempOpenAIAgent" # Changed base name slightly
|
|
|
|
|
|
|
| 27 |
DEFAULT_BATTLE_FORMAT = "gen9randombattle"
|
| 28 |
|
| 29 |
+
# --- Agent Creation (Per Request) ---
|
| 30 |
+
async def create_agent_async(agent_type: str, battle_format: str = DEFAULT_BATTLE_FORMAT) -> Player | str:
|
| 31 |
+
"""
|
| 32 |
+
Creates and initializes a *single* agent instance with a unique username.
|
| 33 |
+
Returns the Player object on success, or an error string on failure.
|
| 34 |
+
"""
|
| 35 |
+
print(f"Attempting to create agent of type: {agent_type}")
|
| 36 |
+
player: Player | None = None
|
| 37 |
+
error_message: str | None = None
|
| 38 |
+
|
| 39 |
+
# Generate a unique suffix for this instance
|
| 40 |
+
agent_suffix = random.randint(10000, 999999) # Wider range for uniqueness
|
| 41 |
+
|
| 42 |
+
try:
|
| 43 |
+
if agent_type == "Random Player":
|
| 44 |
+
username = f"{RANDOM_PLAYER_BASE_NAME}{agent_suffix}"
|
| 45 |
+
account_config = AccountConfiguration(username, None) # Guest account
|
| 46 |
+
print(f"Initializing RandomPlayer with username: {username}")
|
| 47 |
+
player = RandomPlayer(
|
| 48 |
+
account_configuration=account_config,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 49 |
server_configuration=custom_config,
|
| 50 |
+
battle_format=battle_format,
|
| 51 |
+
# Add a start_listening=False initially if needed, manage connection explicitly
|
| 52 |
+
# start_listening=False # Let's try without first
|
| 53 |
)
|
| 54 |
+
# await player.connect() # Explicit connect might be needed if start_listening=False
|
| 55 |
+
|
| 56 |
+
elif agent_type == "OpenAI Agent":
|
| 57 |
+
# Check for API key early
|
| 58 |
+
if not os.getenv("OPENAI_API_KEY"):
|
| 59 |
+
error_message = "Error: Cannot create OpenAI Agent. OPENAI_API_KEY environment variable is missing."
|
| 60 |
+
print(error_message)
|
| 61 |
+
return error_message # Return early
|
| 62 |
|
| 63 |
+
username = f"{OPENAI_AGENT_BASE_NAME}{agent_suffix}"
|
| 64 |
+
account_config = AccountConfiguration(username, None) # Guest account
|
| 65 |
+
print(f"Initializing OpenAIAgent with username: {username}")
|
| 66 |
+
player = OpenAIAgent( # Make sure your OpenAIAgent accepts these args
|
| 67 |
+
account_configuration=account_config,
|
| 68 |
server_configuration=custom_config,
|
| 69 |
+
battle_format=battle_format,
|
| 70 |
+
# start_listening=False
|
| 71 |
)
|
| 72 |
+
# await player.connect()
|
| 73 |
+
|
| 74 |
+
else:
|
| 75 |
+
error_message = f"Error: Invalid agent type '{agent_type}' requested."
|
| 76 |
+
print(error_message)
|
| 77 |
+
return error_message
|
| 78 |
+
|
| 79 |
+
# A short wait might be necessary for the player to establish connection/login
|
| 80 |
+
# This is often handled implicitly by poke-env's internal loops when
|
| 81 |
+
# start_listening=True (default), but managing explicitly can be complex.
|
| 82 |
+
# Let's rely on send_challenges potentially waiting if needed.
|
| 83 |
+
# await asyncio.sleep(2) # Add small delay only if connection issues arise
|
| 84 |
+
|
| 85 |
+
print(f"Agent ({username}) created successfully (object: {player}).")
|
| 86 |
+
return player # Return the player instance
|
| 87 |
+
|
| 88 |
+
except Exception as e:
|
| 89 |
+
agent_name = username if 'username' in locals() else agent_type
|
| 90 |
+
error_message = f"Error creating agent {agent_name}: {e}"
|
| 91 |
+
print(error_message)
|
| 92 |
+
traceback.print_exc() # Log detailed error
|
| 93 |
+
# Ensure cleanup if partial creation occurred (less likely without start_listening=False)
|
| 94 |
+
# if player and hasattr(player, 'disconnect'):
|
| 95 |
+
# try:
|
| 96 |
+
# await player.disconnect()
|
| 97 |
+
# except Exception as disconnect_e:
|
| 98 |
+
# print(f"Error during cleanup disconnect: {disconnect_e}")
|
| 99 |
+
return error_message # Return the error string
|
| 100 |
+
|
| 101 |
+
# --- Battle Invitation Logic (Remains mostly the same, uses the created player) ---
|
| 102 |
+
async def send_battle_invite_async(player: Player, opponent_username: str, battle_format: str):
|
| 103 |
"""Sends a challenge using the provided player object."""
|
| 104 |
+
# Player should already be created and potentially connected by create_agent_async
|
| 105 |
+
if not isinstance(player, Player):
|
| 106 |
+
# This case should ideally be caught earlier, but adding safety check
|
| 107 |
+
return f"Error: Invalid player object passed to send_battle_invite_async: {player}"
|
| 108 |
+
|
| 109 |
+
player_username = getattr(player, 'username', 'unknown_agent') # Get username safely
|
| 110 |
+
|
| 111 |
try:
|
| 112 |
+
print(f"Attempting to send challenge from {player_username} to {opponent_username} in format {battle_format}")
|
| 113 |
+
# Ensure the player's connection is ready if not automatically handled.
|
| 114 |
+
# If using start_listening=False, ensure player.connect() was called and awaited.
|
| 115 |
+
# The send_challenges method might handle waiting for login internally.
|
| 116 |
+
await player.send_challenges(opponent_username, n_challenges=1, packed_team=None, battle_format=battle_format) # Specify format if needed
|
| 117 |
+
print(f"Challenge sent successfully from {player_username} to {opponent_username}.")
|
| 118 |
+
return f"Battle invitation ({battle_format}) sent to {opponent_username} from bot {player_username}! Check Showdown."
|
| 119 |
+
|
| 120 |
except Exception as e:
|
| 121 |
+
print(f"Error sending challenge from {player_username}:")
|
| 122 |
+
traceback.print_exc()
|
| 123 |
+
return f"Error sending challenge from {player_username}: {str(e)}. Check console logs."
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
|
| 125 |
+
# --- No explicit disconnect here - let the scope manage it ---
|
| 126 |
+
# finally:
|
| 127 |
+
# # Attempt to disconnect the temporary agent after the challenge is sent (or fails)
|
| 128 |
+
# if player and hasattr(player, 'disconnect'):
|
| 129 |
+
# print(f"Disconnecting temporary agent: {player_username}")
|
| 130 |
+
# try:
|
| 131 |
+
# await player.disconnect()
|
| 132 |
+
# except Exception as disconnect_e:
|
| 133 |
+
# print(f"Error disconnecting {player_username}: {disconnect_e}")
|
| 134 |
+
|
| 135 |
+
|
| 136 |
+
# --- Gradio Interface Function (Sync Wrapper) ---
|
| 137 |
+
def invite_to_battle(agent_choice: str, username: str):
|
| 138 |
+
"""
|
| 139 |
+
Handles the Gradio button click: Creates an agent, sends invite, and returns status.
|
| 140 |
+
This function is SYNCHRONOUS as required by Gradio's fn handler.
|
| 141 |
+
"""
|
| 142 |
username_clean = username.strip()
|
| 143 |
if not username_clean:
|
| 144 |
return "Please enter your Showdown username."
|
| 145 |
+
if not agent_choice:
|
| 146 |
+
return "Please select an agent type."
|
| 147 |
|
| 148 |
+
# Define the async tasks to be run for this request
|
| 149 |
+
async def _run_async_tasks(selected_agent_type, target_username):
|
| 150 |
+
# 1. Create the agent for this specific request
|
| 151 |
+
agent_or_error = await create_agent_async(selected_agent_type, DEFAULT_BATTLE_FORMAT)
|
| 152 |
+
|
| 153 |
+
if isinstance(agent_or_error, str): # Check if creation returned an error string
|
| 154 |
+
return agent_or_error # Return the error message from creation
|
| 155 |
+
|
| 156 |
+
# 2. If agent created successfully, send the challenge
|
| 157 |
+
player_instance = agent_or_error
|
| 158 |
+
result = await send_battle_invite_async(player_instance, target_username, DEFAULT_BATTLE_FORMAT)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
+
# 3. poke-env usually handles cleanup when the player object goes out of scope
|
| 161 |
+
# or the event loop managing it finishes, especially with asyncio.run.
|
| 162 |
+
# If persistent connection issues arise, explicit player.disconnect() might
|
| 163 |
+
# be needed within send_battle_invite_async's finally block or here.
|
| 164 |
+
print(f"Async task for {getattr(player_instance, 'username', 'agent')} completed.")
|
| 165 |
return result
|
| 166 |
+
|
| 167 |
+
# Run the async tasks within the synchronous Gradio handler
|
| 168 |
+
try:
|
| 169 |
+
# asyncio.run creates a new event loop, runs the coroutine, and closes the loop.
|
| 170 |
+
# This is suitable for managing the short lifecycle of the temporary agent.
|
| 171 |
+
print(f"Starting async task execution for request: {agent_choice} vs {username_clean}")
|
| 172 |
+
result = asyncio.run(_run_async_tasks(agent_choice, username_clean))
|
| 173 |
+
print(f"Async task finished. Result: {result}")
|
| 174 |
+
return result
|
| 175 |
+
except RuntimeError as e:
|
| 176 |
+
# Handle cases where asyncio.run might conflict (e.g., nested loops, rare)
|
| 177 |
+
print(f"RuntimeError during asyncio.run: {e}")
|
| 178 |
+
traceback.print_exc()
|
| 179 |
+
# Check if it's the "cannot run loop while another is running" error
|
| 180 |
+
if "cannot run loop" in str(e):
|
| 181 |
+
return "Error: Could not execute task due to conflicting event loop activity. Please try again."
|
| 182 |
+
else:
|
| 183 |
+
return f"An unexpected runtime error occurred: {e}"
|
| 184 |
except Exception as e:
|
| 185 |
+
print(f"Unexpected error in invite_to_battle sync wrapper: {e}")
|
|
|
|
| 186 |
traceback.print_exc()
|
| 187 |
+
return f"An critical error occurred: {e}"
|
| 188 |
|
| 189 |
|
| 190 |
+
# --- Gradio UI Definition (No changes needed here) ---
|
| 191 |
+
# iframe code to embed Pokemon Showdown (using a potentially more stable client link if needed)
|
| 192 |
iframe_code = """
|
| 193 |
<iframe
|
| 194 |
+
src="https://play.pokemonshowdown.com/"
|
| 195 |
width="100%"
|
| 196 |
height="800"
|
| 197 |
style="border: none;"
|
| 198 |
referrerpolicy="no-referrer">
|
| 199 |
</iframe>
|
| 200 |
"""
|
| 201 |
+
# Note: Changed iframe src to the main client. If your custom server needs a specific
|
| 202 |
+
# client URL like the one you had, change it back. Ensure CORS/embedding is allowed.
|
| 203 |
|
| 204 |
def main_app():
|
| 205 |
"""Creates and returns the Gradio application interface."""
|
| 206 |
+
# NO agent initialization at startup anymore
|
| 207 |
+
# start_agent_initialization() # REMOVED
|
| 208 |
|
|
|
|
| 209 |
with gr.Blocks(title="Pokemon Showdown Agent") as demo:
|
| 210 |
gr.Markdown("# Pokémon Battle Agent")
|
| 211 |
gr.Markdown(
|
| 212 |
"Select an agent, enter **your** Showdown username "
|
| 213 |
+
"(the one you are logged in with below), and click Send Invite. "
|
| 214 |
+
"A temporary bot with a unique name will be created for the challenge."
|
| 215 |
)
|
| 216 |
|
|
|
|
| 217 |
with gr.Row():
|
|
|
|
| 218 |
agent_dropdown = gr.Dropdown(
|
| 219 |
label="Select Agent",
|
| 220 |
choices=["Random Player", "OpenAI Agent"],
|
| 221 |
value="Random Player",
|
| 222 |
+
scale=1
|
| 223 |
)
|
| 224 |
name_input = gr.Textbox(
|
| 225 |
label="Your Pokémon Showdown Username",
|
| 226 |
placeholder="Enter username used in Showdown below",
|
| 227 |
+
scale=2
|
| 228 |
)
|
| 229 |
+
battle_button = gr.Button("Send Battle Invitation", scale=1)
|
| 230 |
|
| 231 |
+
# --- Display area for status/results ---
|
| 232 |
+
status_output = gr.Textbox(label="Status", interactive=False) # Added output field
|
| 233 |
|
|
|
|
| 234 |
gr.Markdown("### Pokémon Showdown Interface")
|
| 235 |
gr.Markdown("Log in/use the username you entered above.")
|
|
|
|
| 236 |
gr.HTML(iframe_code)
|
| 237 |
|
|
|
|
| 238 |
battle_button.click(
|
| 239 |
fn=invite_to_battle,
|
| 240 |
inputs=[agent_dropdown, name_input],
|
| 241 |
+
outputs=[status_output] # Connect output to the status box
|
| 242 |
)
|
| 243 |
|
| 244 |
return demo
|
| 245 |
|
| 246 |
# --- Main execution block ---
|
| 247 |
if __name__ == "__main__":
|
| 248 |
+
# Set OPENAI_API_KEY environment variable if needed, e.g., using python-dotenv
|
| 249 |
+
# from dotenv import load_dotenv
|
| 250 |
+
# load_dotenv()
|
| 251 |
+
# if not os.getenv("OPENAI_API_KEY"):
|
| 252 |
+
# print("Warning: OPENAI_API_KEY not set. OpenAI Agent will not work.")
|
| 253 |
+
|
| 254 |
app = main_app()
|
| 255 |
+
# Consider server_name/port for accessibility if running locally
|
| 256 |
+
# app.launch(server_name="0.0.0.0", server_port=7860)
|
| 257 |
+
app.launch()
|