import gradio as gr import pandas as pd import numpy as np import onnxruntime as ort import sys from pathlib import Path sys.path.append("rd2l_pred") from feature_engineering import heroes, hero_information # Define expected columns EXPECTED_COLUMNS = ['mmr', 'p1', 'p2', 'p3', 'p4', 'p5', 'count', 'mean', 'std', 'min', 'max', 'sum', 'total_games_played', 'total_winrate'] + \ [f'games_{i}' for i in range(1, 146) if i != 24 and i != 122 and i != 124 and i != 125 and i != 127 and i != 130 and i != 132 and i != 133 and i != 134 and i != 139 and i != 140 and i != 141 and i != 142 and i != 143 and i != 144] + \ [f'winrate_{i}' for i in range(1, 146) if i != 24 and i != 122 and i != 124 and i != 125 and i != 127 and i != 130 and i != 132 and i != 133 and i != 134 and i != 139 and i != 140 and i != 141 and i != 142 and i != 143 and i != 144] def prepare_single_player_data(user_id, mmr, comf_1, comf_2, comf_3, comf_4, comf_5): """Creates a DataFrame in the expected format for the model""" try: # Extract player_id from URL if needed player_id = user_id.split("/")[-1] if "/" in user_id else user_id # Get hero statistics using OpenDota API hero_stats = hero_information(player_id) # Create initial data dictionary with zeros for all columns data = {col: 0 for col in EXPECTED_COLUMNS} # Fill in the basic features data.update({ 'mmr': float(mmr), 'p1': int(comf_1), 'p2': int(comf_2), 'p3': int(comf_3), 'p4': int(comf_4), 'p5': int(comf_5), }) # Add hero statistics if hero_stats is not None: data['total_games_played'] = hero_stats.get('total_games_played', 0) data['total_winrate'] = hero_stats.get('total_winrate', 0) # Fill in the games and winrate columns from hero_stats for key, value in hero_stats.items(): if key in EXPECTED_COLUMNS: data[key] = value # Add mock statistics for money-related columns # These would normally come from league_money function stats = { 'count': 1, 'mean': mmr / 200, # rough approximation 'std': mmr / 400, 'min': mmr / 250, 'max': mmr / 150, 'sum': mmr / 200 } data.update(stats) # Convert to DataFrame df = pd.DataFrame([data]) # Ensure columns are in correct order df = df[EXPECTED_COLUMNS] print(f"DataFrame shape: {df.shape}") print("Missing columns:", set(EXPECTED_COLUMNS) - set(df.columns)) return df except Exception as e: print(f"Error in data preparation: {e}") raise e def predict_cost(user_id, mmr, comf_1, comf_2, comf_3, comf_4, comf_5): """Main prediction function for Gradio interface""" try: # Prepare the player data processed_data = prepare_single_player_data(user_id, mmr, comf_1, comf_2, comf_3, comf_4, comf_5) # Load and use the model model_path = Path("model/rd2l_forest.onnx") if not model_path.exists(): return f"Model file not found at: {model_path}" session = ort.InferenceSession(str(model_path)) # Debug information print("Processed data shape:", processed_data.shape) print("Processed data columns:", processed_data.columns.tolist()) # Make prediction input_name = session.get_inputs()[0].name prediction = session.run(None, {input_name: processed_data.values.astype(np.float32)})[0] predicted_cost = round(float(prediction[0]), 2) hero_stats = processed_data.iloc[0] total_games = hero_stats.get('total_games_played', 'N/A') total_winrate = hero_stats.get('total_winrate', 'N/A') return f"""Predicted Cost: {predicted_cost} Player Details: - MMR: {mmr} - Position Comfort: * Pos 1: {comf_1} * Pos 2: {comf_2} * Pos 3: {comf_3} * Pos 4: {comf_4} * Pos 5: {comf_5} Player Statistics: - Total Games: {total_games} - Overall Winrate: {total_winrate:.1%} if isinstance(total_winrate, float) else 'N/A' Note: This prediction is based on historical data and player statistics from OpenDota.""" except Exception as e: return f"Error in prediction pipeline: {str(e)}\n\nDebug info:\n{type(e).__name__}: {str(e)}" # Create Gradio interface demo = gr.Interface( fn=predict_cost, inputs=[ gr.Textbox(label="Player ID or Link to OpenDota/Dotabuff", placeholder="Enter player ID or full profile URL"), gr.Number(label="MMR", value=3000), gr.Slider(1, 5, value=3, step=1, label="Comfort (Pos 1)"), gr.Slider(1, 5, value=3, step=1, label="Comfort (Pos 2)"), gr.Slider(1, 5, value=3, step=1, label="Comfort (Pos 3)"), gr.Slider(1, 5, value=3, step=1, label="Comfort (Pos 4)"), gr.Slider(1, 5, value=3, step=1, label="Comfort (Pos 5)") ], examples=[ ["https://www.dotabuff.com/players/188649776", 6812, 5, 5, 4, 2, 1] ], outputs=gr.Textbox(label="Prediction Results"), title="RD2L Player Cost Predictor", description="""This tool predicts the auction cost for RD2L players based on their MMR, position comfort levels, and historical performance data from OpenDota. Enter a player's OpenDota ID or profile URL along with their current stats to get a predicted cost.""", article="""### How it works - The predictor uses machine learning trained on historical RD2L draft data - Player statistics are fetched from OpenDota API - Position comfort levels range from 1 (least comfortable) to 5 (most comfortable) - Predictions are based on both current stats and historical performance ### Notes - MMR should be the player's current solo MMR - Position comfort should reflect actual role experience - Predictions are estimates and may vary from actual draft results""" ) if __name__ == "__main__": demo.launch()