import openai import gradio as gr import requests import os #from config import OPENAI_API_KEY # SETUP OPENAI KEY from external file openai.api_key = os.getenv("OPEN_API_KEY") # System prompt to ensure strategy-focused responses STRATEGY_SYSTEM_PROMPT = ( "You are a highly experienced VALORANT strategist. Provide detailed tactical strategies " "focusing on map control, utility usage, timing, and team coordination. Do NOT just list agent abilities; " "focus on actionable plays, specific callouts, and step-by-step plans." ) # API endpoints for agent and map details VALORANT_AGENTS_API = "https://valorant-api.com/v1/agents?isPlayableCharacter=true" VALORANT_MAPS_API = "https://valorant-api.com/v1/maps" # Allowed Valorant maps in your UI ALLOWED_MAPS = [ "Ascent", "Abyss", "Bind", "Breeze", "Fracture", "Haven", "Icebox", "Lotus", "Pearl", "Split", "Sunset" ] # Fetch playable agents at startup (fallback to a minimal list if it fails) try: agents_resp = requests.get(VALORANT_AGENTS_API).json() AGENT_NAMES = [ a["displayName"] for a in agents_resp.get("data", []) if a.get("isPlayableCharacter", False) ] if not AGENT_NAMES: raise ValueError("No agents fetched") except Exception: AGENT_NAMES = [ "Astra", "Breach", "Brimstone", "Chamber", "Clove", "Cypher", "Deadlock", "Fade", "Gekko", "Harbor", "Iso", "Jett", "Kay/o", "Killjoy", "Omen", "Phoenix", "Raze", "Reyna", "Sage", "Sova", "Skye", "Tejo", "Viper", "Waylay", "Yoru", "Vyse" ] # Fetch map data at startup, but only keep the allowed ones try: maps_resp = requests.get(VALORANT_MAPS_API).json().get("data", []) # build a dict of map displayName -> full map object MAP_DATA = { m["displayName"]: m for m in maps_resp if m.get("displayName") in ALLOWED_MAPS } # list of names for the dropdown MAP_NAMES = list(MAP_DATA.keys()) if not MAP_NAMES: raise ValueError("No allowed maps fetched from API") except Exception: # fallback if API fails MAP_DATA = {name: {"callouts": []} for name in ALLOWED_MAPS} MAP_NAMES = ALLOWED_MAPS WIKI_API_URL = "https://en.wikipedia.org/w/api.php" def fetch_agent_wiki(agent_name): """ Fallback: fetch summary from Wikipedia for an agent. """ try: res = requests.get( WIKI_API_URL, params={ "action": "query", "prop": "extracts", "exintro": True, "format": "json", "titles": agent_name } ).json() page = next(iter(res["query"]["pages"].values())) return page.get("extract", "") except Exception: return "" def generate_valorant_strategy(messages, model="gpt-4"): try: res = openai.ChatCompletion.create( model=model, messages=messages, temperature=0.7, max_tokens=700 ) return res.choices[0].message.content.strip() except Exception as e: return f"❌ ERROR generating strategy: {e}" def on_click_strategy(map_choice, side_choice, round_goal, site_choice, economy_choice, playstyle_choice, agent_choice, auto_comp): """ Builds the prompt, fetches agent info (API + fallback), generates a strategy, and validates that any callouts mentioned actually exist on the chosen map. """ # Determine agents if auto_comp: comp_prompt = [ {"role": "system", "content": STRATEGY_SYSTEM_PROMPT}, {"role": "user", "content": ( f"For VALORANT map '{map_choice}' at {site_choice.lower()} site on the " f"{side_choice.lower()} side with {economy_choice.lower()} economy and a round goal of " f"{round_goal}, propose a balanced team of five agents, at least one Controller. " "List only the agent names separated by commas." )} ] comp_res = generate_valorant_strategy(comp_prompt) agent_list = [a.strip() for a in comp_res.split(",")] else: agent_list = agent_choice # Build initial strategy prompt user_content = ( f"On the {side_choice.lower()} side of {map_choice} at {site_choice.lower()} site " f"with a {economy_choice.lower()} economy and a round goal of {round_goal}, " f"generate a {playstyle_choice.lower()} strategy using agents: {', '.join(agent_list)}." ) messages = [ {"role": "system", "content": STRATEGY_SYSTEM_PROMPT}, {"role": "user", "content": user_content} ] # First pass strategy = generate_valorant_strategy(messages) # Validate callouts all_callouts = { callout.get("regionName") for m in MAP_DATA.values() for callout in m.get("callouts", []) if callout.get("regionName") } allowed = { callout.get("regionName") for callout in MAP_DATA.get(map_choice, {}).get("callouts", []) if callout.get("regionName") } used = {c for c in all_callouts if c and c in strategy} invalid = used - allowed if invalid: correction = ( f"Your strategy mentioned callouts not on {map_choice}: {', '.join(invalid)}. " f"Only use these callouts for {map_choice}: {', '.join(sorted(allowed))}. " "Please regenerate the full strategy." ) messages.append({"role": "user", "content": correction}) strategy = generate_valorant_strategy(messages) return strategy css = """ :root { --valorant-white: #ffffff; } /* Default (dark mode) styling */ .gradio-container { background-image: url('https://path.to/valorant-background.jpg'); background-size: cover; background-position: center; padding: 30px; } .gradio-blocks { backdrop-filter: blur(8px); } #header-title { text-align: center; color: var(--valorant-white); font-size: 2.8em; font-weight: 800; margin-bottom: 10px; text-shadow: 3px 3px 10px rgba(0, 0, 0, 0.9); } #subtext { text-align: center; color: var(--valorant-white); font-size: 1em; margin-bottom: 20px; } .section { background-color: rgba(15, 15, 15, 0.85); padding: 20px; border-radius: 8px; margin-bottom: 20px; } .section-title { font-size: 1.2em; font-weight: 700; margin-bottom: 10px; color: var(--valorant-white); } #generate-btn { background-color: #FF4655; color: white; padding: 12px 24px; font-weight: 700; box-shadow: 0 3px 8px rgba(0, 0, 0, 0.6); margin-top: 10px; } #generate-btn:hover { background-color: #E8434C; } /* Light mode overrides */ @media (prefers-color-scheme: light) { #header-title, #subtext, .section-title { color: #000000; text-shadow: 3px 3px 10px rgba(255, 255, 255, 0.7); } .section { background-color: rgba(255, 255, 255, 0.85); } } """ with gr.Blocks(css=css, title="VALORANT Strategy Generator") as ui: gr.HTML("