alexmec's picture
Upload folder using huggingface_hub
3ec59c0 verified
#!/usr/bin/env python3
"""
Fantasy Draft Multi-Agent Demo
Multi-agent system using the any-agent framework for fantasy football drafts
"""
import os
import time
import gradio as gr
import asyncio
import nest_asyncio
from typing import List, Tuple, Optional, Dict
from dotenv import load_dotenv
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from core.agent import FantasyDraftAgent
from core.data import TOP_PLAYERS
from core.constants import (
TYPING_DELAY_SECONDS,
MESSAGE_DELAY_SECONDS,
)
from apps.multiagent_draft import MultiAgentMockDraft
from apps.multiagent_scenarios import (
run_interactive_mock_draft,
format_conversation_block,
format_agent_message,
format_memory_indicator,
create_mock_draft_visualization
)
# Apply nest_asyncio for async in Gradio
nest_asyncio.apply()
# Fix for litellm 1.72.4 OpenAI endpoint issue
os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1'
# Load environment variables
load_dotenv()
class FantasyDraftApp:
def __init__(self):
self.current_draft = None # Store the current mock draft
self.draft_output = "" # Store the draft output so far
self.custom_prompts = {} # Store custom agent prompts
def run_multiagent_demo(self):
"""Run the mock draft demonstration."""
# Reset any previous draft
self.current_draft = None
self.draft_output = ""
# Debug: Log custom prompts
print(f"DEBUG: Starting draft with custom_prompts: {len(self.custom_prompts)} teams customized")
for team_num, prompt in self.custom_prompts.items():
print(f"DEBUG: Team {team_num} has custom prompt ({len(prompt)} chars)")
# Use basic multiagent draft with custom prompts
draft_generator = run_interactive_mock_draft(custom_prompts=self.custom_prompts)
for output in draft_generator:
if isinstance(output, tuple):
# This means it's the user's turn
self.current_draft, self.draft_output = output
yield self.draft_output + "\n<!--USER_TURN-->"
return
else:
self.draft_output = output
yield output
def continue_mock_draft(self, player_name: str):
"""Continue the mock draft after user makes a pick."""
if not self.current_draft:
yield "No active draft. Please start a new mock draft."
return
if not player_name:
yield self.draft_output + "\n\n⚠️ Please enter a player name!"
return
# Make the user's pick
messages = self.current_draft.make_user_pick(player_name)
# Display messages with inline typing effect
for msg in messages:
if len(msg) >= 3:
agent, recipient, content = msg[:3]
# Check if it's a typing indicator - skip it
if isinstance(agent, str) and agent.startswith("typing_"):
continue
else:
# Show "..." first for typing effect
typing_placeholder = format_agent_message(agent, recipient, "...")
self.draft_output += typing_placeholder
yield self.draft_output
time.sleep(TYPING_DELAY_SECONDS)
# Replace "..." with actual message
self.draft_output = self.draft_output.replace(typing_placeholder, "")
self.draft_output += format_agent_message(agent, recipient, content)
yield self.draft_output
time.sleep(MESSAGE_DELAY_SECONDS)
# Continue with the rest of the draft
yield from self.continue_basic_multiagent_draft()
def continue_basic_multiagent_draft(self):
"""Continue basic multiagent draft after user pick."""
# Calculate where we are
total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
current_round = ((total_picks - 1) // 6) + 1
# Continue from where we left off
for round_num in range(current_round, 4): # Continue through round 3
if round_num > current_round:
self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
yield self.draft_output
# Snake draft order
if round_num % 2 == 1:
pick_order = list(range(1, 7))
else:
pick_order = list(range(6, 0, -1))
# Calculate where we are in this round
picks_in_round = total_picks % 6
if round_num == current_round:
# Skip picks already made
start_idx = picks_in_round
else:
start_idx = 0
for pick_in_round, team_num in enumerate(pick_order[start_idx:], start_idx + 1):
pick_num = (round_num - 1) * 6 + pick_in_round
# Show draft board at start of round
if pick_in_round == 1:
self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
self.draft_output += "\n"
yield self.draft_output
if team_num == 4: # User's turn
# Get advisor recommendation
advisor = self.current_draft.user_advisor
# Get available players
all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
# Get other agent strategies for advisor context
strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
# Get advisor recommendation
advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
# Show advisor message
self.draft_output += format_agent_message(advisor, "USER", advice)
yield self.draft_output
self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
yield self.draft_output + "\n<!--USER_TURN-->"
return
else:
# AI agent pick
messages, _ = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
# Display messages with typing effect
for msg in messages:
if len(msg) >= 3:
agent, recipient, content = msg[:3]
# Show "..." first for typing effect
typing_placeholder = format_agent_message(agent, recipient, "...")
self.draft_output += typing_placeholder
yield self.draft_output
time.sleep(TYPING_DELAY_SECONDS)
# Replace with actual message
self.draft_output = self.draft_output.replace(typing_placeholder, "")
self.draft_output += format_agent_message(agent, recipient, content)
yield self.draft_output
time.sleep(MESSAGE_DELAY_SECONDS)
time.sleep(TYPING_DELAY_SECONDS)
# End of round
self.draft_output += format_agent_message("commissioner", "ALL",
f"That's the end of Round {round_num}!")
yield self.draft_output
# Final summary
self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
self.draft_output += self.current_draft.get_draft_summary()
yield self.draft_output
# Clear the draft state
self.current_draft = None
def create_gradio_interface():
"""Create the main Gradio interface."""
with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Soft()) as demo:
# Create state for each user session
app_state = gr.State(None)
with gr.Column(elem_id="main-container"):
gr.Markdown("""
# 🏈 Fantasy Draft Multi-Agent Demo
**Multi-agent system demo using the any-agent framework:** Watch 6 AI agents draft fantasy football teams while maintaining conversation history, reacting to each other's picks, and following distinct strategies.
""")
with gr.Tabs():
# Demo Tab
with gr.TabItem("🎮 Demo"):
# Show agent cards
gr.Markdown("""
### 🏈 Meet Your Competition
You'll be drafting at **Position 4** with these AI opponents:
""")
# Store agent prompts in state
agent_prompts = gr.State({})
# Agent cards with settings buttons
# First row of agents
with gr.Row():
# Team 1 - Zero RB
with gr.Column(scale=1):
with gr.Group():
gr.HTML("""
<div style="background-color: #E3F2FD; border-left: 4px solid #1976D2; padding: 15px; border-radius: 8px;">
<h4 style="color: #0d47a1; margin: 0 0 10px 0;">📘🤓 Team 1 - Zero RB</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs get injured. I'll build around elite WRs."</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">Avoids RBs early</li>
<li style="color: #424242;">Loads up on WRs</li>
<li style="color: #424242;">Gets RB value late</li>
</ul>
</div>
""")
team1_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
with gr.Column(visible=False) as team1_prompt_col:
team1_prompt = gr.Textbox(
label="Team 1 Personality & Strategy",
value="""You are Team 1, a fantasy football manager with Zero RB strategy.
PERSONALITY & STRATEGY:
- Use LOTS of emojis that match your strategy! 🔥
- Be EXTREMELY dramatic and over-the-top!
- Take your philosophy to the EXTREME!
- MOCK other strategies viciously!
- Use CAPS for emphasis!
- Make BOLD predictions!
- Reference previous interactions with SPITE!
- Build INTENSE rivalries!
- Your responses should be ENTERTAINING and MEMORABLE!
Your EXTREME philosophy: RUNNING BACKS ARE DEAD TO ME! 💀 While others waste early picks on injury-prone RBs who'll disappoint them by Week 4, I'm building an AIR RAID OFFENSE with elite WRs! 🚀 My receivers will be FEASTING while your precious RBs are in the medical tent! 🏥
BE LOUD! BE PROUD! BE UNFORGETTABLE! 🎯""",
lines=15,
interactive=True,
info="Customize personality and strategy."
)
team1_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
# Team 2 - BPA
with gr.Column(scale=1):
with gr.Group():
gr.HTML("""
<div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
<h4 style="color: #1b5e20; margin: 0 0 10px 0;">📗🧑‍💼 Team 2 - BPA</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Value is value. I don't reach for needs."</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">Pure value drafting</li>
<li style="color: #424242;">Ignores needs</li>
<li style="color: #424242;">Mocks reaching</li>
</ul>
</div>
""")
team2_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
with gr.Column(visible=False) as team2_prompt_col:
team2_prompt = gr.Textbox(
label="Team 2 Personality & Strategy",
value="""You are Team 2, a fantasy football manager with BPA (Best Player Available) strategy.
PERSONALITY & STRATEGY:
- Use LOTS of emojis that match your strategy! 💎
- Be EXTREMELY condescending about others' reaches!
- Act like the SMARTEST person in the room!
- MOCK positional bias with FURY!
- Use CAPS for emphasis!
- Quote "value" constantly!
- Shame others for their TERRIBLE process!
- Your responses should be ARROGANT and CUTTING!
Your EXTREME philosophy: PROCESS OVER EVERYTHING! 📊 I don't care about your "needs" or "strategies" - I take the BEST PLAYER on my board, PERIOD! 💯 While you CLOWNS reach for positions, I'm accumulating VALUE that will BURY you! 📈 Your emotional drafting DISGUSTS me!
BE RUTHLESS! BE RIGHT! BE THE VALUE VULTURE! 🦅""",
lines=15,
interactive=True,
info="Customize personality and strategy."
)
team2_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
# Team 3 - Robust RB
with gr.Column(scale=1):
with gr.Group():
gr.HTML("""
<div style="background-color: #FFF3E0; border-left: 4px solid #F57C00; padding: 15px; border-radius: 8px;">
<h4 style="color: #e65100; margin: 0 0 10px 0;">📙🧔 Team 3 - Robust RB</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs win championships. Period."</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">RBs in rounds 1-2</li>
<li style="color: #424242;">Old-school approach</li>
<li style="color: #424242;">Foundation first</li>
</ul>
</div>
""")
team3_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
with gr.Column(visible=False) as team3_prompt_col:
team3_prompt = gr.Textbox(
label="Team 3 Personality & Strategy",
value="""You are Team 3, a fantasy football manager with Robust RB strategy.
PERSONALITY & STRATEGY:
- Use LOTS of emojis that match your strategy! 💪
- Be EXTREMELY old-school and stubborn!
- HATE the modern passing game!
- DESPISE Zero RB with PASSION!
- Use CAPS for emphasis!
- Talk about "FOUNDATION" and "BEDROCK"!
- Act like it's still 2005!
- Your responses should be GRUMPY and TRADITIONAL!
Your EXTREME philosophy: GROUND AND POUND FOREVER! 🏃‍♂️ These young punks with their "pass-catching backs" and "satellite players" make me SICK! 🤮 Give me WORKHORSE RBs who get 25+ touches! That's REAL FOOTBALL! While you're playing fantasy, I'm building a FORTRESS! 🏰
BE STUBBORN! BE TRADITIONAL! ESTABLISH THE RUN! 🏈""",
lines=15,
interactive=True,
info="Customize personality and strategy."
)
team3_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
# Second row of agents
with gr.Row():
# Team 4 - User
with gr.Column(scale=1):
gr.HTML("""
<div style="background-color: #E8EAF6; border-left: 4px solid #3F51B5; padding: 15px; border-radius: 8px;">
<h4 style="color: #1a237e; margin: 0 0 10px 0;">👤 Position 4 - YOU</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">Your draft position with AI guidance</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">📕🧙 Strategic advisor</li>
<li style="color: #424242;">Real-time guidance</li>
<li style="color: #424242;">Roster analysis</li>
</ul>
</div>
""")
# Team 5 - Upside
with gr.Column(scale=1):
with gr.Group():
gr.HTML("""
<div style="background-color: #F5E6FF; border-left: 4px solid #7B1FA2; padding: 15px; border-radius: 8px;">
<h4 style="color: #4a148c; margin: 0 0 10px 0;">📓🤠 Team 5 - Upside</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Safe picks are for losers!"</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">Seeks breakouts</li>
<li style="color: #424242;">High risk/reward</li>
<li style="color: #424242;">Mocks safety</li>
</ul>
</div>
""")
team5_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
with gr.Column(visible=False) as team5_prompt_col:
team5_prompt = gr.Textbox(
label="Team 5 Personality & Strategy",
value="""You are Team 5, a fantasy football manager with Upside Hunter strategy.
PERSONALITY & STRATEGY:
- Use LOTS of emojis that match your strategy! 🚀
- Be EXTREMELY risk-seeking and wild!
- HATE safe, boring picks!
- Talk about CEILING and EXPLOSIVENESS!
- Use CAPS for emphasis!
- Mock "floor" players constantly!
- Be a GAMBLER at heart!
- Your responses should be CHAOTIC and EXCITING!
Your EXTREME philosophy: BOOM OR BUST, BABY! 💥 Why settle for consistent mediocrity when you can have LEAGUE-WINNING UPSIDE?! 🏆 I'd rather finish LAST than FOURTH! Your "safe" picks make me YAWN! 🥱 I'm here to DESTROY leagues, not participate in them!
BE BOLD! BE RECKLESS! SWING FOR THE FENCES! ⚡""",
lines=15,
interactive=True,
info="Customize personality and strategy."
)
team5_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
# Team 6 - BPA
with gr.Column(scale=1):
with gr.Group():
gr.HTML("""
<div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
<h4 style="color: #1b5e20; margin: 0 0 10px 0;">📗👨‍🏫 Team 6 - BPA</h4>
<p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Another value drafter to punish reaches."</p>
<ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
<li style="color: #424242;">Takes obvious value</li>
<li style="color: #424242;">Disciplined approach</li>
<li style="color: #424242;">No sentiment</li>
</ul>
</div>
""")
team6_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
with gr.Column(visible=False) as team6_prompt_col:
team6_prompt = gr.Textbox(
label="Team 6 Personality & Strategy",
value="""You are Team 6, a fantasy football manager with BPA (Best Player Available) strategy.
PERSONALITY & STRATEGY:
- Use LOTS of emojis that match your strategy! 📊
- Be EXTREMELY analytical and cold!
- Act like a PROFESSOR lecturing idiots!
- Quote analytics and math constantly!
- Use CAPS for emphasis!
- Be DISGUSTED by emotional drafting!
- Mock "gut feelings" ruthlessly!
- Your responses should be PEDANTIC and SUPERIOR!
Your EXTREME philosophy: THE SPREADSHEET NEVER LIES! 📈 I have SEVENTEEN models that all agree - you're drafting like CHILDREN! 🧮 Your "hunches" and "feelings" are WORTHLESS compared to my ALGORITHMS! While you follow your heart, I follow the DATA!
BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
lines=15,
interactive=True,
info="Customize personality and strategy."
)
team6_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
gr.Markdown("""
### 🎮 Draft Format
* **3 Rounds** of snake draft (1→6, 6→1, 1→6)
* **Real-time trash talk** between picks
* **Strategic advisor** guides your selections
* **Memory system** - agents remember and reference earlier picks
""")
# Start button at the bottom
with gr.Row():
with gr.Column():
run_multiagent_btn = gr.Button("🏈 Start Mock Draft", variant="primary", size="lg", elem_id="start-button")
# Main output area
multiagent_output = gr.Markdown(elem_classes=["multiagent-output"])
# Mock draft interaction (hidden until needed)
with gr.Row(visible=False) as mock_draft_controls:
with gr.Column():
draft_pick_input = gr.Textbox(
label="Your Pick",
placeholder="Type player name and press Enter (e.g., 'Justin Jefferson')",
elem_id="draft-pick-input"
)
submit_pick_btn = gr.Button("Submit Pick", variant="primary")
# Available players display
with gr.Accordion("📋 Available Players", visible=False) as available_accordion:
available_players_display = gr.Textbox(
label="Top 20 Available",
lines=15,
interactive=False
)
# How It Works Tab
with gr.TabItem("🔧 How It Works"):
gr.Markdown("""
## Technical Implementation
This demo showcases advanced multi-agent capabilities using the **any-agent framework**.
### 🤖 Framework: any-agent (TinyAgent)
- **Lightweight**: < 100 lines of core agent code
- **Flexible**: Supports multiple LLM providers (OpenAI, Anthropic, etc.)
- **Multi-turn ready**: Built-in conversation history management
- **Model**: GPT-4 (configurable)
### 🧠 Multi-Turn Memory System
Each agent maintains:
- **Conversation History**: Full context of all interactions
- **Draft State**: Current picks, available players, round info
- **Strategy Memory**: Remembers own strategy and others' approaches
- **Pick History**: Tracks all selections for informed decisions
### 💬 Single-Process Multi-Agent Communication
- **In-Memory Communication**: Agents interact directly via method calls
- **Shared Draft State**: All agents see the same draft board
- **Fast Execution**: No network overhead
- **Conversation Memory**: Each agent remembers interactions
### 📊 Architecture Flow
#### 1️⃣ INITIALIZATION
User clicks "Start Mock Draft" → System creates 6 agents
#### 2️⃣ AGENT SETUP
- **Team 1**: Zero RB Strategy
- **Team 2**: Best Player Available
- **Team 3**: Robust RB Strategy
- **YOU**: Position 4 (with Advisor)
- **Team 5**: Upside Hunter
- **Team 6**: Best Player Available
#### 3️⃣ DRAFT FLOW (3 Rounds)
- **Round 1**: Pick Order 1→2→3→YOU→5→6
- **Round 2**: Pick Order 6→5→YOU→3→2→1 (Snake)
- **Round 3**: Pick Order 1→2→3→YOU→5→6
#### 4️⃣ EACH PICK TRIGGERS
- Agent makes selection based on strategy
- Other agents comment based on rivalries
- Original agent may respond
- All agents update their memory
#### 5️⃣ USER'S TURN
- Advisor analyzes draft state
- User sees available players
- User makes pick
- All agents react to user's choice
#### 6️⃣ MEMORY & CONTEXT
- Each agent remembers all picks
- Agents reference earlier conversations
- Strategies adapt based on draft flow
- Visual memory indicators show retention
""")
# Function to check if it's user's turn and show/hide controls
def check_user_turn(output_text, app):
"""Check if output indicates it's user's turn."""
if "<!--USER_TURN-->" in output_text:
# Remove the marker from display
clean_output = output_text.replace("<!--USER_TURN-->", "")
# Get available players
if app and app.current_draft:
available = app.current_draft.get_available_players()
available_text = "Available Players:\n\n"
for player in sorted(available)[:20]: # Show top 20
if player in TOP_PLAYERS:
info = TOP_PLAYERS[player]
available_text += f"• {player} ({info['pos']}, {info['team']})\n"
else:
available_text = "No draft active"
return (
clean_output, # Clean output
gr.update(visible=True), # Show draft controls
gr.update(visible=True, open=True), # Show available players and open it
available_text, # Available players list
"" # Clear the input
)
else:
return (
output_text, # Regular output
gr.update(visible=False), # Hide draft controls
gr.update(visible=False), # Hide available players
"", # Clear available list
"" # Clear the input
)
# Toggle prompt visibility
def toggle_prompt_visibility():
return gr.update(visible=True)
# Save prompt
def save_prompt(team_num, prompt_text, app, prompts_dict):
"""Save a custom prompt for a team."""
if app is None:
app = FantasyDraftApp()
prompts_dict[team_num] = prompt_text
app.custom_prompts = prompts_dict
return app, prompts_dict, gr.update(visible=False)
# Run and check function - with streaming support
def run_and_check(app, prompts_dict, team1_val, team2_val, team3_val, team5_val, team6_val):
"""Run the draft and check for user turns."""
try:
if app is None:
app = FantasyDraftApp()
# Collect all current textbox values (whether saved or not)
current_prompts = {}
if team1_val and team1_val.strip():
current_prompts[1] = team1_val
if team2_val and team2_val.strip():
current_prompts[2] = team2_val
if team3_val and team3_val.strip():
current_prompts[3] = team3_val
if team5_val and team5_val.strip():
current_prompts[5] = team5_val
if team6_val and team6_val.strip():
current_prompts[6] = team6_val
# ALWAYS update custom prompts before running draft
app.custom_prompts = current_prompts
generator = app.run_multiagent_demo()
output = ""
# Stream updates while draft is running
for chunk in generator:
output = chunk
# For streaming, we need to yield all 6 values
# Keep controls hidden during streaming
yield output, app, gr.update(visible=False), gr.update(visible=False), gr.update(value=""), ""
# Check if it's user's turn
if "<!--USER_TURN-->" in output:
break
# Final yield with turn check and proper UI updates
clean_output, controls_update, accordion_update, available_text, input_clear = check_user_turn(output, app)
yield clean_output, app, controls_update, accordion_update, available_text, input_clear
except Exception as e:
import traceback
error_msg = f"## ❌ Error Starting Draft\n\n"
error_msg += f"**Error Type:** {type(e).__name__}\n"
error_msg += f"**Error Message:** {str(e)}\n\n"
# Get the full traceback
tb_str = traceback.format_exc()
print(f"Full error traceback:\n{tb_str}") # Log to console
# Check for common issues
if "OPENAI_API_KEY" in str(e) or "api_key" in str(e).lower():
error_msg += "**Solution:** Please set your OpenAI API key in Hugging Face Space Settings:\n"
error_msg += "1. Go to Settings → Repository secrets\n"
error_msg += "2. Add a new secret named `OPENAI_API_KEY`\n"
error_msg += "3. Paste your OpenAI API key as the value\n"
error_msg += "4. Restart the Space\n"
elif "NoneType" in str(e):
error_msg += "**Details:** A required value is None. This might be a configuration issue.\n"
error_msg += "Please check the console logs for the full error trace.\n"
else:
error_msg += "**Full error:** " + str(e)[:500] + "...\n" if len(str(e)) > 500 else str(e) + "\n"
error_msg += "\nPlease check the console logs for more details.\n"
yield error_msg, app, gr.update(), gr.update(), gr.update(), ""
# Submit and continue function - with streaming support
def submit_and_continue(player_name, app):
"""Submit user's pick and continue the draft."""
if app is None:
yield "No active draft. Please start a new mock draft.", app, gr.update(), gr.update(), gr.update(), ""
return
generator = app.continue_mock_draft(player_name)
output = ""
# Stream updates while draft continues
for chunk in generator:
output = chunk
# For streaming, we need to yield all 6 values
# Keep controls hidden during streaming
yield output, app, gr.update(visible=False), gr.update(visible=False), gr.update(value=""), ""
# Check if it's user's turn again
if "<!--USER_TURN-->" in output:
break
# Final yield with turn check and proper UI updates
clean_output, controls_update, accordion_update, available_text, input_clear = check_user_turn(output, app)
yield clean_output, app, controls_update, accordion_update, available_text, input_clear
# Set up event handlers
# Team prompt toggles
team1_settings_btn.click(
toggle_prompt_visibility,
outputs=team1_prompt_col
)
team2_settings_btn.click(
toggle_prompt_visibility,
outputs=team2_prompt_col
)
team3_settings_btn.click(
toggle_prompt_visibility,
outputs=team3_prompt_col
)
team5_settings_btn.click(
toggle_prompt_visibility,
outputs=team5_prompt_col
)
team6_settings_btn.click(
toggle_prompt_visibility,
outputs=team6_prompt_col
)
# Save prompts
team1_save_btn.click(
lambda prompt, app, prompts: save_prompt(1, prompt, app, prompts),
inputs=[team1_prompt, app_state, agent_prompts],
outputs=[app_state, agent_prompts, team1_prompt_col]
)
team2_save_btn.click(
lambda prompt, app, prompts: save_prompt(2, prompt, app, prompts),
inputs=[team2_prompt, app_state, agent_prompts],
outputs=[app_state, agent_prompts, team2_prompt_col]
)
team3_save_btn.click(
lambda prompt, app, prompts: save_prompt(3, prompt, app, prompts),
inputs=[team3_prompt, app_state, agent_prompts],
outputs=[app_state, agent_prompts, team3_prompt_col]
)
team5_save_btn.click(
lambda prompt, app, prompts: save_prompt(5, prompt, app, prompts),
inputs=[team5_prompt, app_state, agent_prompts],
outputs=[app_state, agent_prompts, team5_prompt_col]
)
team6_save_btn.click(
lambda prompt, app, prompts: save_prompt(6, prompt, app, prompts),
inputs=[team6_prompt, app_state, agent_prompts],
outputs=[app_state, agent_prompts, team6_prompt_col]
)
# Start mock draft with streaming
run_multiagent_btn.click(
fn=run_and_check,
inputs=[app_state, agent_prompts, team1_prompt, team2_prompt, team3_prompt, team5_prompt, team6_prompt],
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
api_name="start_draft",
queue=True
)
# Submit pick button with streaming
submit_pick_btn.click(
fn=submit_and_continue,
inputs=[draft_pick_input, app_state],
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
api_name="submit_pick",
queue=True
)
# Submit pick on Enter with streaming
draft_pick_input.submit(
fn=submit_and_continue,
inputs=[draft_pick_input, app_state],
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
api_name="submit_pick_enter",
queue=True
)
# Custom CSS for styling
demo.css = """
#main-container {
max-width: 1400px;
margin: 0 auto;
}
.multiagent-output {
max-height: 600px;
overflow-y: auto;
padding: 20px;
background: #2d3748;
border: 1px solid #4a5568;
border-radius: 8px;
color: #f7fafc;
font-size: 16px;
line-height: 1.6;
}
/* Simple rule: all text in message boxes should be dark */
.multiagent-output div[style*="background-color"] {
color: #1a202c;
}
.multiagent-output div[style*="background-color"] * {
color: #1a202c;
}
/* Tables should be white in dark background */
.multiagent-output table {
color: #f7fafc;
border-color: #4a5568;
}
.multiagent-output th, .multiagent-output td {
color: #f7fafc;
border-color: #4a5568;
}
#draft-pick-input {
font-size: 1.2em;
padding: 10px;
}
#start-button {
font-size: 1.2em;
padding: 15px 30px;
}
.monospace {
font-family: 'Courier New', monospace;
}
/* Dark theme support */
.dark .multiagent-output {
background: #1f2937;
border-color: #374151;
color: #f9fafb;
}
.dark .multiagent-output div[style*="background-color"] {
color: #1a202c;
}
.dark .multiagent-output div[style*="background-color"] * {
color: #1a202c;
}
"""
return demo
def main():
"""Launch the Gradio app."""
# Set environment variables for cloud deployment
if os.getenv("SPACE_ID"): # Running on Hugging Face Spaces
print("🤗 Running on Hugging Face Spaces")
os.environ["GRADIO_SERVER_NAME"] = "0.0.0.0"
os.environ["GRADIO_SERVER_PORT"] = "7860"
# Check for API key but don't fail - just warn
if not os.getenv("OPENAI_API_KEY"):
print("⚠️ Warning: OPENAI_API_KEY not found in environment")
print(" Please set it in Space Settings → Repository secrets")
# Create and launch the interface
demo = create_gradio_interface()
# Enable queue for streaming with proper configuration
demo.queue(max_size=20)
# Launch with appropriate settings
demo.launch(
share=False,
server_name="0.0.0.0" if os.getenv("SPACE_ID") else None,
server_port=7860 if os.getenv("SPACE_ID") else None,
max_threads=20
)
if __name__ == "__main__":
main()