Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| """ | |
| Fantasy Draft Multi-Agent Demo | |
| Showcases multi-agent and multi-turn capabilities | |
| """ | |
| import os | |
| import time | |
| import gradio as gr | |
| from typing import List, Tuple | |
| from dotenv import load_dotenv | |
| import sys | |
| import os | |
| 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 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 | |
| ) | |
| # Fix for litellm 1.72.4 OpenAI endpoint issue | |
| # This ensures litellm uses the correct OpenAI API endpoint | |
| os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1' | |
| # Load environment variables from .env file | |
| 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 | |
| def run_multiagent_demo(self): | |
| """Run the mock draft demonstration.""" | |
| # Reset any previous draft | |
| self.current_draft = None | |
| self.draft_output = "" | |
| # Run the draft generator | |
| draft_generator = run_interactive_mock_draft() | |
| for output in draft_generator: | |
| # Check if this is a tuple (draft state, output) | |
| if isinstance(output, tuple): | |
| # This means it's the user's turn | |
| self.current_draft, self.draft_output = output | |
| # Add a special marker for Gradio to detect | |
| yield self.draft_output + "\n<!--USER_TURN-->" | |
| return | |
| else: | |
| # Regular output | |
| 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 # Skip typing indicators, we'll handle inline | |
| else: | |
| # Show "..." first for typing effect | |
| typing_placeholder = format_agent_message(agent, recipient, "...") | |
| self.draft_output += typing_placeholder | |
| yield self.draft_output | |
| time.sleep(0.5) # Brief typing delay | |
| # 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(1.0) # Reading delay | |
| # Continue the draft from where we left off | |
| # We need to track where we were in the draft | |
| total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks]) | |
| current_round = ((total_picks - 1) // 6) + 1 # 6 teams per round | |
| # Continue with the rest of the draft | |
| draft_memories = [] | |
| # Continue the draft | |
| for round_num in range(current_round, 4): # Continue from current round to round 3 | |
| if round_num > current_round: | |
| self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n" | |
| yield self.draft_output | |
| # Snake draft order - 6 teams total | |
| if round_num % 2 == 1: | |
| pick_order = list(range(1, 7)) # 1-6 for odd rounds | |
| else: | |
| pick_order = list(range(6, 0, -1)) # 6-1 for even rounds | |
| # Calculate where we are in the current round | |
| picks_in_round = total_picks % 6 # 6 teams per round | |
| start_idx = picks_in_round if round_num == current_round else 0 | |
| for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1): | |
| pick_num = (round_num - 1) * 6 + pick_in_round # 6 teams per 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 | |
| # Process the pick | |
| messages, result = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num) | |
| # 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 # Skip typing indicators, we'll handle inline | |
| else: | |
| # Show "..." first for typing effect | |
| typing_placeholder = format_agent_message(agent, recipient, "...") | |
| self.draft_output += typing_placeholder | |
| yield self.draft_output | |
| time.sleep(0.5) # Brief typing delay | |
| # 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(1.0) # Reading delay | |
| if result is None: | |
| # It's the user's turn again | |
| self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n" | |
| yield self.draft_output + "\n<!--USER_TURN-->" | |
| return | |
| # Add memory indicators | |
| if round_num > 1 and pick_in_round % 2 == 0: | |
| if team_num in self.current_draft.agents: | |
| agent = self.current_draft.agents[team_num] | |
| if len(agent.picks) > 1: | |
| memory = f"{agent.team_name} has drafted: {', '.join(agent.picks)}" | |
| draft_memories.append(memory) | |
| if draft_memories: | |
| self.draft_output += format_memory_indicator(round_num, draft_memories[-2:]) | |
| yield self.draft_output | |
| time.sleep(0.5) | |
| # 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.""" | |
| app = FantasyDraftApp() | |
| with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Glass()) as demo: | |
| with gr.Column(elem_id="main-container"): | |
| gr.Markdown(""" | |
| # 🏈 Fantasy Draft Multi-Agent Demo | |
| **Experience the future of AI interaction:** Watch 6 intelligent agents compete in a fantasy football draft with distinct strategies, real-time trash talk, and persistent memory. | |
| """) | |
| with gr.Tabs(): | |
| # Demo Tab | |
| with gr.TabItem("🎮 Demo"): | |
| # Show agent cards first | |
| gr.Markdown(""" | |
| ### 🏈 Meet Your Competition | |
| You'll be drafting at **Position 4** with these AI opponents: | |
| """) | |
| # Agent cards in a grid - all in one row | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #E3F2FD; border-left: 4px solid #1976D2; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">📘🤓 Team 1 - Zero RB</h4> | |
| <p style="color: #1976D2 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs get injured. I'll build around elite WRs."</p> | |
| <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #1a237e !important;">Avoids RBs early</li> | |
| <li style="color: #1a237e !important;">Loads up on WRs</li> | |
| <li style="color: #1a237e !important;">Gets RB value late</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗🧑💼 Team 2 - BPA</h4> | |
| <p style="color: #2e7d32 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Value is value. I don't reach for needs."</p> | |
| <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #1b5e20 !important;">Pure value drafting</li> | |
| <li style="color: #1b5e20 !important;">Ignores needs</li> | |
| <li style="color: #1b5e20 !important;">Mocks reaching</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #FFF3E0; border-left: 4px solid #F57C00; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #e65100 !important; margin: 0 0 10px 0;">📙🧔 Team 3 - Robust RB</h4> | |
| <p style="color: #ef6c00 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs win championships. Period."</p> | |
| <ul style="color: #e65100 !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #e65100 !important;">RBs in rounds 1-2</li> | |
| <li style="color: #e65100 !important;">Old-school approach</li> | |
| <li style="color: #e65100 !important;">Foundation first</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #E8EAF6; border-left: 4px solid #3F51B5; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">👤 Position 4 - YOU</h4> | |
| <p style="color: #3949ab !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">Your draft position with AI guidance</p> | |
| <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #1a237e !important;">📕🧙 Strategic advisor</li> | |
| <li style="color: #1a237e !important;">Real-time guidance</li> | |
| <li style="color: #1a237e !important;">Roster analysis</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #F5E6FF; border-left: 4px solid #7B1FA2; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #4a148c !important; margin: 0 0 10px 0;">📓🤠 Team 5 - Upside</h4> | |
| <p style="color: #6a1b9a !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Safe picks are for losers!"</p> | |
| <ul style="color: #4a148c !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #4a148c !important;">Seeks breakouts</li> | |
| <li style="color: #4a148c !important;">High risk/reward</li> | |
| <li style="color: #4a148c !important;">Mocks safety</li> | |
| </ul> | |
| </div> | |
| """) | |
| with gr.Column(scale=1): | |
| gr.Markdown(""" | |
| <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;"> | |
| <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗👨🏫 Team 6 - BPA</h4> | |
| <p style="color: #2e7d32 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Another value drafter to punish reaches."</p> | |
| <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;"> | |
| <li style="color: #1b5e20 !important;">Takes obvious value</li> | |
| <li style="color: #1b5e20 !important;">Disciplined approach</li> | |
| <li style="color: #1b5e20 !important;">No sentiment</li> | |
| </ul> | |
| </div> | |
| """) | |
| 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 | |
| Ready to experience the most realistic AI draft room? | |
| """) | |
| # 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 | |
| ### 💬 Agent-to-Agent (A2A) Communication | |
| Agents can: | |
| - **Comment on picks**: React to other agents' selections | |
| - **Respond to comments**: Defend their strategies | |
| - **Remember debates**: Reference earlier conversations | |
| - **Adapt strategies**: Adjust based on draft flow | |
| ### 📊 Architecture Flow | |
| """) | |
| gr.Markdown(""" | |
| #### 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 (A2A communication) | |
| - 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 | |
| """) | |
| gr.Markdown(""" | |
| ### 🎯 Key Features Demonstrated | |
| 1. **Persistent Context**: Each agent remembers all previous interactions | |
| 2. **Strategic Personalities**: 5 distinct draft strategies competing | |
| 3. **Dynamic Adaptation**: Agents adjust based on draft progression | |
| 4. **Natural Dialogue**: Human-like commentary and debates | |
| 5. **User Integration**: Seamless human participation with AI guidance | |
| ### 📝 Implementation Details | |
| - **Agent Classes**: Inheritance-based design with base `DraftAgent` | |
| - **Message Formatting**: Custom HTML/CSS for visual distinction | |
| - **State Management**: Draft board tracking and validation | |
| - **Memory Indicators**: Visual cues showing context retention | |
| ### 🚀 Why This Matters | |
| This demo proves that sophisticated multi-agent systems can be built with minimal code, | |
| showcasing the power of modern LLMs when properly orchestrated. The any-agent framework | |
| makes it easy to create agents that truly communicate and remember, not just respond. | |
| """) | |
| # Function to check if it's user's turn and show/hide controls | |
| def check_user_turn(output_text): | |
| """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.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 | |
| ) | |
| # Run multi-agent demo with control visibility handling | |
| def run_and_check(): | |
| """Run demo and check for user turn.""" | |
| for output in app.run_multiagent_demo(): | |
| result = check_user_turn(output) | |
| yield result | |
| run_multiagent_btn.click( | |
| run_and_check, | |
| None, | |
| [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input], | |
| show_progress=True | |
| ) | |
| # Continue draft after user pick | |
| def submit_and_continue(player_name): | |
| """Submit pick and continue draft.""" | |
| for output in app.continue_mock_draft(player_name): | |
| result = check_user_turn(output) | |
| yield result | |
| submit_pick_btn.click( | |
| submit_and_continue, | |
| draft_pick_input, | |
| [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input], | |
| show_progress=True | |
| ) | |
| # Also submit on enter | |
| draft_pick_input.submit( | |
| submit_and_continue, | |
| draft_pick_input, | |
| [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input], | |
| show_progress=True | |
| ) | |
| # Add custom CSS for better styling | |
| demo.css = """ | |
| /* Force white text on dark background for all main content */ | |
| .gradio-container { | |
| max-width: 800px !important; | |
| margin: 0 auto !important; | |
| color: white !important; | |
| } | |
| /* Ensure all text elements are white by default */ | |
| .gradio-container p, | |
| .gradio-container h1, | |
| .gradio-container h2, | |
| .gradio-container h3, | |
| .gradio-container h4, | |
| .gradio-container h5, | |
| .gradio-container h6, | |
| .gradio-container span, | |
| .gradio-container div, | |
| .gradio-container label, | |
| .gradio-container .markdown, | |
| .gradio-container .prose { | |
| color: white !important; | |
| } | |
| /* Ensure markdown content is white */ | |
| .markdown-text, | |
| .markdown-text p, | |
| .markdown-text li, | |
| .markdown-text ul, | |
| .markdown-text ol { | |
| color: white !important; | |
| } | |
| #main-container { | |
| text-align: center; | |
| color: white !important; | |
| } | |
| /* Left-align text in How It Works tab */ | |
| .tabitem:nth-child(2) { | |
| text-align: left !important; | |
| } | |
| #start-button { | |
| margin: 20px auto !important; | |
| max-width: 300px !important; | |
| } | |
| /* Only force dark text inside colored message boxes */ | |
| div[style*="background-color"][style*="border-left"] { | |
| color: #212121 !important; | |
| } | |
| div[style*="background-color"][style*="border-left"] p, | |
| div[style*="background-color"][style*="border-left"] strong, | |
| div[style*="background-color"][style*="border-left"] em, | |
| div[style*="background-color"][style*="border-left"] span, | |
| div[style*="background-color"][style*="border-left"] li, | |
| div[style*="background-color"][style*="border-left"] ul, | |
| div[style*="background-color"][style*="border-left"] h1, | |
| div[style*="background-color"][style*="border-left"] h2, | |
| div[style*="background-color"][style*="border-left"] h3, | |
| div[style*="background-color"][style*="border-left"] h4 { | |
| color: #212121 !important; | |
| } | |
| /* System messages with yellow background */ | |
| div[style*="#FFF9C4"] { | |
| color: #F57C00 !important; | |
| } | |
| /* Memory boxes */ | |
| div[style*="#F5F5F5"] { | |
| color: #424242 !important; | |
| } | |
| #draft-pick-input { | |
| font-size: 1.1em; | |
| } | |
| /* Ensure tab labels are visible */ | |
| .tab-nav button { | |
| color: white !important; | |
| } | |
| /* Ensure multiagent output text is white */ | |
| .multiagent-output { | |
| color: white !important; | |
| } | |
| .multiagent-output p, | |
| .multiagent-output h1, | |
| .multiagent-output h2, | |
| .multiagent-output h3, | |
| .multiagent-output h4, | |
| .multiagent-output h5, | |
| .multiagent-output h6, | |
| .multiagent-output span, | |
| .multiagent-output div { | |
| color: white !important; | |
| } | |
| /* Specific rules for dark mode - target Gradio's dark theme class */ | |
| .dark .gradio-container, | |
| .dark .gradio-container *:not([style*="background-color"]) { | |
| color: white !important; | |
| } | |
| /* Ensure description text under title is white */ | |
| .gradio-container > div > div > div > p { | |
| color: white !important; | |
| } | |
| /* Tab content text */ | |
| .tabitem .markdown-text { | |
| color: white !important; | |
| } | |
| /* Input labels and text */ | |
| .gradio-container label { | |
| color: rgba(255, 255, 255, 0.9) !important; | |
| } | |
| /* Button text that's not in primary buttons */ | |
| button:not(.primary) { | |
| color: rgba(255, 255, 255, 0.9) !important; | |
| } | |
| /* Fix for bold player names - ensure they're visible */ | |
| .multiagent-output strong, | |
| .multiagent-output b { | |
| color: inherit !important; | |
| font-weight: 700; | |
| } | |
| /* Ensure bold text in message backgrounds has proper color */ | |
| div[style*="background-color"] strong, | |
| div[style*="background-color"] b { | |
| color: inherit !important; | |
| } | |
| /* Specific fix for bold text in different message backgrounds */ | |
| div[style*="background-color: #E3F2FD"] strong, /* Team messages */ | |
| div[style*="background-color: #FFF8E1"] strong, /* Commissioner */ | |
| div[style*="background-color: #FFEBEE"] strong, /* Advisor */ | |
| div[style*="background-color: #F3E5F5"] strong { /* Memory */ | |
| color: #1a1a1a !important; | |
| } | |
| /* Inline typing effect */ | |
| .typing-dots { | |
| animation: pulse 1.0s ease-in-out infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 0.6; } | |
| 50% { opacity: 1; } | |
| } | |
| """ | |
| return demo | |
| def main(): | |
| """Launch the Gradio app.""" | |
| import sys | |
| # Check for --share flag | |
| share_mode = "--share" in sys.argv or "-s" in sys.argv | |
| # Check for API key | |
| if not os.getenv("OPENAI_API_KEY"): | |
| print("\n⚠️ Warning: OPENAI_API_KEY not found in environment variables.") | |
| print("The app will launch but agent responses will fail without an API key.") | |
| print("Set it using: export OPENAI_API_KEY='your-key-here'\n") | |
| print("🚀 Launching Fantasy Draft Multi-Agent Demo...") | |
| if share_mode: | |
| print("🌐 Creating public share link (expires in 72 hours)...") | |
| print("📡 Please wait for the public URL...\n") | |
| else: | |
| print("📡 The app will be available at http://localhost:7860") | |
| print("💡 Tip: Use 'python app.py --share' to create a public link\n") | |
| demo = create_gradio_interface() | |
| demo.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| share=share_mode, # Enable sharing if flag is present | |
| show_error=True | |
| ) | |
| if __name__ == "__main__": | |
| main() |