Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
Upload folder using huggingface_hub
Browse files- README.md +106 -21
- app.py +2 -117
- apps/app.py +155 -811
- apps/multiagent_draft.py +1 -1
- core/constants.py +2 -49
- docs/FEATURES_AND_ENHANCEMENTS.md +137 -197
- requirements.txt +4 -17
README.md
CHANGED
@@ -11,35 +11,120 @@ license: mit
|
|
11 |
python_version: 3.11
|
12 |
---
|
13 |
|
14 |
-
# 🏈 Fantasy
|
15 |
|
16 |
-
|
17 |
|
18 |
-
## Features
|
19 |
|
20 |
-
- **
|
21 |
-
- **
|
22 |
-
|
23 |
-
|
24 |
-
- **
|
25 |
-
- **
|
26 |
-
- **
|
27 |
|
28 |
-
##
|
29 |
|
30 |
-
|
31 |
-
|
|
|
32 |
|
33 |
-
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
- **Simulated A2A**: Mock distributed experience (fallback)
|
40 |
|
41 |
-
|
|
|
|
|
42 |
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
|
45 |
[View Source Code](https://github.com/alexmeckes/fantasydraft)
|
|
|
11 |
python_version: 3.11
|
12 |
---
|
13 |
|
14 |
+
# 🏈 Fantasy Football Multi-Agent Draft System
|
15 |
|
16 |
+
An interactive fantasy football draft simulation featuring 6 AI agents with distinct personalities and strategies, built using the `any-agent` framework.
|
17 |
|
18 |
+
## ✨ Features
|
19 |
|
20 |
+
- **Interactive Mock Draft**: Play as Team 4 with 5 AI opponents
|
21 |
+
- **Distinct Agent Strategies**: Zero RB, Robust RB, Best Player Available, Upside Hunter
|
22 |
+
- **Multi-Turn Memory**: Agents remember picks and conversations throughout the draft
|
23 |
+
- **Agent Communication**: Agents comment on and respond to each other's picks
|
24 |
+
- **Strategic Advisor**: AI advisor helps you make informed draft decisions
|
25 |
+
- **Visual Draft Board**: Track all picks with a beautiful, real-time updated interface
|
26 |
+
- **Customizable Personalities**: Modify each agent's personality and trash-talk style
|
27 |
|
28 |
+
## 🚀 Quick Start
|
29 |
|
30 |
+
### Prerequisites
|
31 |
+
- Python 3.8+
|
32 |
+
- OpenAI API key
|
33 |
|
34 |
+
### Installation
|
35 |
|
36 |
+
```bash
|
37 |
+
# Clone the repository
|
38 |
+
git clone <your-repo-url>
|
39 |
+
cd fantasy-draft-agent
|
|
|
40 |
|
41 |
+
# Create virtual environment
|
42 |
+
python -m venv venv
|
43 |
+
source venv/bin/activate # On Windows: venv\Scripts\activate
|
44 |
|
45 |
+
# Install dependencies
|
46 |
+
pip install -r requirements.txt
|
47 |
+
|
48 |
+
# Set your OpenAI API key
|
49 |
+
export OPENAI_API_KEY='your-key-here' # On Windows: set OPENAI_API_KEY=your-key-here
|
50 |
+
```
|
51 |
+
|
52 |
+
### Running the Application
|
53 |
+
|
54 |
+
```bash
|
55 |
+
cd apps
|
56 |
+
python app.py
|
57 |
+
```
|
58 |
+
|
59 |
+
Then open your browser to `http://localhost:7860`
|
60 |
+
|
61 |
+
## 🎮 How to Play
|
62 |
+
|
63 |
+
1. **Start the Draft**: Click "🏈 Start Mock Draft"
|
64 |
+
2. **Watch AI Picks**: Agents draft in snake order with commentary
|
65 |
+
3. **Make Your Pick**: When it's your turn (Position 4), type a player name
|
66 |
+
4. **Get Advice**: Your AI advisor provides recommendations based on available players
|
67 |
+
5. **Enjoy the Banter**: Watch agents trash-talk and defend their strategies
|
68 |
+
|
69 |
+
## 🤖 The Agents
|
70 |
+
|
71 |
+
- **Team 1 - Zero RB** 📘🤓: Avoids RBs early, loads up on WRs
|
72 |
+
- **Team 2 - BPA** 📗🧑💼: Pure value drafting, mocks reaching
|
73 |
+
- **Team 3 - Robust RB** 📙🧔: Old-school RB-heavy approach
|
74 |
+
- **Team 4 - YOU** 👤: Your position with AI advisor
|
75 |
+
- **Team 5 - Upside Hunter** 📓🤠: High risk/reward picks
|
76 |
+
- **Team 6 - BPA** 📗👨🏫: Another value drafter
|
77 |
+
|
78 |
+
## 🛠️ Technical Details
|
79 |
+
|
80 |
+
Built with:
|
81 |
+
- **any-agent**: Lightweight multi-agent framework
|
82 |
+
- **Gradio**: Interactive web interface
|
83 |
+
- **OpenAI GPT-4**: Powers agent decision-making
|
84 |
+
- **Multi-turn memory**: Agents maintain conversation history
|
85 |
+
|
86 |
+
## 📁 Project Structure
|
87 |
+
|
88 |
+
```
|
89 |
+
fantasy-draft-agent/
|
90 |
+
├── apps/
|
91 |
+
│ ├── app.py # Main Gradio interface
|
92 |
+
│ ├── multiagent_draft.py # Core draft logic
|
93 |
+
│ └── multiagent_scenarios.py # Agent communication scenarios
|
94 |
+
├── core/
|
95 |
+
│ ├── agent.py # Base agent implementation
|
96 |
+
│ ├── data.py # Player data and rankings
|
97 |
+
│ └── constants.py # Configuration constants
|
98 |
+
└── requirements.txt
|
99 |
+
```
|
100 |
+
|
101 |
+
## 🎯 Key Features Explained
|
102 |
+
|
103 |
+
### Multi-Agent System
|
104 |
+
- Each agent runs independently with its own strategy
|
105 |
+
- Agents maintain memory of all interactions
|
106 |
+
- Natural rivalries create engaging commentary
|
107 |
+
|
108 |
+
### Interactive Gameplay
|
109 |
+
- Real-time draft board visualization
|
110 |
+
- Strategic advisor provides contextual recommendations
|
111 |
+
- Customizable agent personalities
|
112 |
+
|
113 |
+
### Memory System
|
114 |
+
- Agents remember previous picks and conversations
|
115 |
+
- References to earlier interactions create continuity
|
116 |
+
- Strategic adaptation based on draft flow
|
117 |
+
|
118 |
+
## 🚦 Deployment
|
119 |
+
|
120 |
+
The application is designed to run locally or on cloud platforms like Hugging Face Spaces. The single-process architecture ensures reliable performance without complex networking requirements.
|
121 |
+
|
122 |
+
## 📝 License
|
123 |
+
|
124 |
+
[Your License Here]
|
125 |
+
|
126 |
+
## 🤝 Contributing
|
127 |
+
|
128 |
+
Contributions welcome! Please feel free to submit a Pull Request.
|
129 |
|
130 |
[View Source Code](https://github.com/alexmeckes/fantasydraft)
|
app.py
CHANGED
@@ -1,132 +1,17 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
-
Entry point for
|
4 |
-
All dependencies should be installed via requirements.txt.
|
5 |
"""
|
6 |
|
7 |
import os
|
8 |
import sys
|
9 |
|
10 |
-
#
|
11 |
-
from datetime import datetime
|
12 |
-
print(f"\n===== Application Startup at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} =====\n")
|
13 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
14 |
-
import typing_patch
|
15 |
|
16 |
# Simple startup message
|
17 |
if os.getenv("SPACE_ID"):
|
18 |
print("🤗 Running on Hugging Face Spaces...")
|
19 |
-
|
20 |
-
# Check A2A installation
|
21 |
-
print("🔍 Checking A2A installation...")
|
22 |
-
|
23 |
-
# Check any-agent version
|
24 |
-
try:
|
25 |
-
import any_agent
|
26 |
-
print(f"✅ any-agent version: {any_agent.__version__}")
|
27 |
-
except ImportError as e:
|
28 |
-
print(f"❌ any-agent not installed: {e}")
|
29 |
-
|
30 |
-
# Check for httpx
|
31 |
-
try:
|
32 |
-
import httpx
|
33 |
-
print(f"✅ httpx version: {httpx.__version__}")
|
34 |
-
except ImportError as e:
|
35 |
-
print(f"❌ httpx not installed: {e}")
|
36 |
-
|
37 |
-
# Check for a2a module
|
38 |
-
try:
|
39 |
-
import a2a
|
40 |
-
import importlib.metadata
|
41 |
-
a2a_version = importlib.metadata.version('a2a-sdk')
|
42 |
-
print(f"✅ a2a module found (a2a-sdk version: {a2a_version})")
|
43 |
-
except ImportError as e:
|
44 |
-
print(f"❌ a2a module not found: {e}")
|
45 |
-
|
46 |
-
# Check if any_agent.serving exists
|
47 |
-
try:
|
48 |
-
import any_agent.serving
|
49 |
-
print("✅ any_agent.serving module found")
|
50 |
-
except ImportError as e:
|
51 |
-
print(f"❌ any_agent.serving module not found: {e}")
|
52 |
-
|
53 |
-
# Check for AgentSkill specifically
|
54 |
-
try:
|
55 |
-
from a2a.types import AgentSkill
|
56 |
-
print("✅ AgentSkill import successful")
|
57 |
-
except ImportError as e:
|
58 |
-
print(f"❌ AgentSkill import failed: {e}")
|
59 |
-
|
60 |
-
# Check for server dependencies
|
61 |
-
print("\n🔍 Checking server dependencies:")
|
62 |
-
try:
|
63 |
-
import uvicorn
|
64 |
-
print(f"✅ uvicorn found: {uvicorn.__version__}")
|
65 |
-
except ImportError as e:
|
66 |
-
print(f"❌ uvicorn not found: {e}")
|
67 |
-
|
68 |
-
try:
|
69 |
-
import starlette
|
70 |
-
print(f"✅ starlette found: {starlette.__version__}")
|
71 |
-
except ImportError as e:
|
72 |
-
print(f"❌ starlette not found: {e}")
|
73 |
-
|
74 |
-
try:
|
75 |
-
from a2a.server import apps
|
76 |
-
print("✅ a2a.server.apps found")
|
77 |
-
except ImportError as e:
|
78 |
-
print(f"❌ a2a.server.apps not found: {e}")
|
79 |
-
|
80 |
-
try:
|
81 |
-
import sse_starlette
|
82 |
-
print("✅ sse-starlette found")
|
83 |
-
except ImportError as e:
|
84 |
-
print(f"❌ sse-starlette not found: {e}")
|
85 |
-
|
86 |
-
# Test the specific imports that any_agent.serving tries
|
87 |
-
print("\n🔍 Testing any_agent.serving imports:")
|
88 |
-
|
89 |
-
# Test A2AServingConfig import
|
90 |
-
try:
|
91 |
-
from any_agent.serving.a2a.config_a2a import A2AServingConfig
|
92 |
-
print("✅ A2AServingConfig direct import successful")
|
93 |
-
except ImportError as e:
|
94 |
-
print(f"❌ A2AServingConfig direct import failed: {e}")
|
95 |
-
|
96 |
-
# Test server_a2a imports
|
97 |
-
try:
|
98 |
-
from any_agent.serving.a2a.server_a2a import serve_a2a
|
99 |
-
print("✅ serve_a2a direct import successful")
|
100 |
-
except ImportError as e:
|
101 |
-
print(f"❌ serve_a2a direct import failed: {e}")
|
102 |
-
|
103 |
-
# Test other imports from server_a2a
|
104 |
-
try:
|
105 |
-
from any_agent.serving.a2a.server_a2a import (
|
106 |
-
_get_a2a_app,
|
107 |
-
_get_a2a_app_async,
|
108 |
-
serve_a2a_async,
|
109 |
-
)
|
110 |
-
print("✅ All server_a2a imports successful")
|
111 |
-
except ImportError as e:
|
112 |
-
print(f"❌ server_a2a imports failed: {e}")
|
113 |
-
import traceback
|
114 |
-
traceback.print_exc()
|
115 |
-
|
116 |
-
# Try to import A2AServingConfig with more detailed error handling
|
117 |
-
try:
|
118 |
-
from any_agent.serving import A2AServingConfig
|
119 |
-
print("✅ A2AServingConfig import successful")
|
120 |
-
except ImportError as e:
|
121 |
-
print(f"❌ Import error: {e}")
|
122 |
-
# Try to get more details
|
123 |
-
try:
|
124 |
-
import traceback
|
125 |
-
print("Full traceback:")
|
126 |
-
traceback.print_exc()
|
127 |
-
except:
|
128 |
-
pass
|
129 |
-
|
130 |
else:
|
131 |
print("🖥️ Running locally...")
|
132 |
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
+
Entry point for the Fantasy Draft Multi-Agent application.
|
|
|
4 |
"""
|
5 |
|
6 |
import os
|
7 |
import sys
|
8 |
|
9 |
+
# Add current directory to path
|
|
|
|
|
10 |
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
11 |
|
12 |
# Simple startup message
|
13 |
if os.getenv("SPACE_ID"):
|
14 |
print("🤗 Running on Hugging Face Spaces...")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
else:
|
16 |
print("🖥️ Running locally...")
|
17 |
|
apps/app.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
-
Fantasy Draft Multi-Agent Demo
|
4 |
-
|
5 |
"""
|
6 |
|
7 |
import os
|
@@ -12,7 +12,7 @@ import nest_asyncio
|
|
12 |
from typing import List, Tuple, Optional, Dict
|
13 |
from dotenv import load_dotenv
|
14 |
import sys
|
15 |
-
|
16 |
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
17 |
|
18 |
from core.agent import FantasyDraftAgent
|
@@ -20,21 +20,7 @@ from core.data import TOP_PLAYERS
|
|
20 |
from core.constants import (
|
21 |
TYPING_DELAY_SECONDS,
|
22 |
MESSAGE_DELAY_SECONDS,
|
23 |
-
AGENT_START_DELAY,
|
24 |
-
AGENT_STARTUP_WAIT,
|
25 |
-
DEFAULT_TIMEOUT,
|
26 |
-
MAX_COMMENTS_PER_PICK,
|
27 |
-
RIVAL_PAIRS,
|
28 |
-
AGENT_CONFIGS
|
29 |
-
)
|
30 |
-
from core.a2a_helpers import (
|
31 |
-
parse_a2a_response,
|
32 |
-
extract_task_id,
|
33 |
-
format_available_players
|
34 |
)
|
35 |
-
# Lazy import A2A components to avoid import errors on HF Spaces
|
36 |
-
DynamicA2AAgentManager = None
|
37 |
-
cleanup_session = None
|
38 |
|
39 |
from apps.multiagent_draft import MultiAgentMockDraft
|
40 |
from apps.multiagent_scenarios import (
|
@@ -45,9 +31,6 @@ from apps.multiagent_scenarios import (
|
|
45 |
create_mock_draft_visualization
|
46 |
)
|
47 |
|
48 |
-
# A2A components will be imported lazily when needed
|
49 |
-
# to avoid import errors on Hugging Face Spaces
|
50 |
-
|
51 |
# Apply nest_asyncio for async in Gradio
|
52 |
nest_asyncio.apply()
|
53 |
|
@@ -58,314 +41,30 @@ os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1'
|
|
58 |
load_dotenv()
|
59 |
|
60 |
|
61 |
-
class
|
62 |
def __init__(self):
|
63 |
self.current_draft = None # Store the current mock draft
|
64 |
self.draft_output = "" # Store the draft output so far
|
65 |
-
self.a2a_manager = None # Will be created dynamically with session ID
|
66 |
-
self.use_real_a2a = False
|
67 |
-
self.a2a_status = "Not initialized"
|
68 |
-
self.session_id = None
|
69 |
self.custom_prompts = {} # Store custom agent prompts
|
70 |
|
71 |
-
|
72 |
-
"""
|
73 |
-
self.use_real_a2a = use_a2a
|
74 |
-
|
75 |
-
if use_a2a:
|
76 |
-
# Lazy import A2A components only when needed
|
77 |
-
try:
|
78 |
-
global DynamicA2AAgentManager, cleanup_session
|
79 |
-
from core.dynamic_a2a_manager import DynamicA2AAgentManager, cleanup_session
|
80 |
-
self.real_a2a = True
|
81 |
-
self.a2a_type = "full"
|
82 |
-
except ImportError as e:
|
83 |
-
# Fall back to simulated A2A
|
84 |
-
try:
|
85 |
-
from core.simulated_a2a_manager import SimulatedA2AAgentManager, cleanup_session
|
86 |
-
DynamicA2AAgentManager = SimulatedA2AAgentManager
|
87 |
-
self.real_a2a = False
|
88 |
-
self.a2a_type = "simulated"
|
89 |
-
print("Using simulated A2A mode (real A2A not available)")
|
90 |
-
except ImportError as e2:
|
91 |
-
self.a2a_status = f"❌ A2A mode not available: {str(e)}. Please use Basic Multiagent mode."
|
92 |
-
self.use_real_a2a = False
|
93 |
-
return self.a2a_status
|
94 |
-
|
95 |
-
# Generate unique session ID if needed
|
96 |
-
if not self.session_id:
|
97 |
-
import uuid
|
98 |
-
self.session_id = str(uuid.uuid4())[:8]
|
99 |
-
|
100 |
-
# Create new dynamic manager for this session with custom prompts
|
101 |
-
self.a2a_manager = DynamicA2AAgentManager(
|
102 |
-
self.session_id,
|
103 |
-
custom_prompts=self.custom_prompts
|
104 |
-
)
|
105 |
-
|
106 |
-
try:
|
107 |
-
await self.a2a_manager.start_agents()
|
108 |
-
ports = self.a2a_manager.allocated_ports
|
109 |
-
if hasattr(self, 'a2a_type'):
|
110 |
-
if self.a2a_type == "full":
|
111 |
-
self.a2a_status = f"✅ Full A2A Mode Active (Session: {self.session_id}, Ports: {ports[0]}-{ports[-1]})"
|
112 |
-
elif self.a2a_type == "lightweight":
|
113 |
-
self.a2a_status = f"✅ Lightweight A2A Mode Active (Session: {self.session_id}, HTTP Ports: {ports[0]}-{ports[-1]})"
|
114 |
-
else: # simulated
|
115 |
-
self.a2a_status = f"✅ Simulated A2A Mode Active (Session: {self.session_id}, Mock Ports: {ports[0]}-{ports[-1]})"
|
116 |
-
else:
|
117 |
-
self.a2a_status = f"✅ A2A Mode Active (Session: {self.session_id}, Ports: {ports[0]}-{ports[-1]})"
|
118 |
-
except RuntimeError as e:
|
119 |
-
# Failed to allocate ports or start agents
|
120 |
-
self.a2a_status = f"❌ Failed to start A2A: {str(e)}"
|
121 |
-
self.use_real_a2a = False
|
122 |
-
self.a2a_manager = None
|
123 |
-
else:
|
124 |
-
if self.a2a_manager and cleanup_session:
|
125 |
-
await cleanup_session(self.a2a_manager)
|
126 |
-
self.a2a_manager = None
|
127 |
-
self.a2a_status = "✅ Basic Multiagent Mode Active (Using built-in communication)"
|
128 |
-
|
129 |
-
return self.a2a_status
|
130 |
-
|
131 |
-
def run_multiagent_demo(self, use_a2a: bool = False):
|
132 |
-
"""Run the mock draft demonstration with optional A2A support."""
|
133 |
# Reset any previous draft
|
134 |
self.current_draft = None
|
135 |
self.draft_output = ""
|
136 |
|
137 |
-
#
|
138 |
-
|
139 |
-
asyncio.set_event_loop(loop)
|
140 |
-
|
141 |
-
status = loop.run_until_complete(self.toggle_a2a_mode(use_a2a))
|
142 |
-
yield f"**Mode:** {status}\n\n"
|
143 |
-
|
144 |
-
# Initialize draft
|
145 |
-
self.current_draft = MultiAgentMockDraft(user_pick_position=4)
|
146 |
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
for output in draft_generator:
|
155 |
-
if isinstance(output, tuple):
|
156 |
-
# This means it's the user's turn
|
157 |
-
self.current_draft, self.draft_output = output
|
158 |
-
yield self.draft_output + "\n<!--USER_TURN-->"
|
159 |
-
return
|
160 |
-
else:
|
161 |
-
self.draft_output = output
|
162 |
-
yield output
|
163 |
-
|
164 |
-
def run_a2a_draft(self):
|
165 |
-
"""Run draft with A2A communication."""
|
166 |
-
# Initialize draft
|
167 |
-
self.current_draft = MultiAgentMockDraft(user_pick_position=4)
|
168 |
-
self.draft_output = "# 🏈 Mock Draft with A2A Communication\n\n"
|
169 |
-
|
170 |
-
# Welcome message
|
171 |
-
if hasattr(self, 'a2a_type'):
|
172 |
-
if self.a2a_type == "full":
|
173 |
-
welcome_msg = "Welcome to the A2A-powered draft! Each agent is running on its own server with full A2A protocol."
|
174 |
-
elif self.a2a_type == "lightweight":
|
175 |
-
welcome_msg = "Welcome to the lightweight A2A draft! Each agent runs on its own HTTP server (no grpcio needed)."
|
176 |
-
else: # simulated
|
177 |
-
welcome_msg = "Welcome to the simulated A2A draft! Agents communicate using mock HTTP calls."
|
178 |
-
else:
|
179 |
-
welcome_msg = "Welcome to the A2A-powered draft! Each agent is running on its own server."
|
180 |
-
self.draft_output += format_agent_message(
|
181 |
-
"commissioner", "ALL",
|
182 |
-
welcome_msg
|
183 |
-
)
|
184 |
-
yield self.draft_output
|
185 |
-
|
186 |
-
# Run draft rounds
|
187 |
-
loop = asyncio.get_event_loop()
|
188 |
-
|
189 |
-
for round_num in range(1, 4): # 3 rounds
|
190 |
-
self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
|
191 |
-
yield self.draft_output
|
192 |
-
|
193 |
-
# Snake draft order
|
194 |
-
if round_num % 2 == 1:
|
195 |
-
pick_order = list(range(1, 7))
|
196 |
else:
|
197 |
-
|
198 |
-
|
199 |
-
for pick_in_round, team_num in enumerate(pick_order, 1):
|
200 |
-
pick_num = (round_num - 1) * 6 + pick_in_round
|
201 |
-
|
202 |
-
# Show draft board at start of round
|
203 |
-
if pick_in_round == 1:
|
204 |
-
self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
|
205 |
-
self.draft_output += "\n"
|
206 |
-
yield self.draft_output
|
207 |
-
|
208 |
-
if team_num == 4: # User's turn
|
209 |
-
# Get advisor recommendation - use user_advisor directly
|
210 |
-
advisor = self.current_draft.user_advisor
|
211 |
-
|
212 |
-
# Get available players
|
213 |
-
all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
|
214 |
-
available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
|
215 |
-
|
216 |
-
# Get other agent strategies for advisor context
|
217 |
-
strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
|
218 |
-
|
219 |
-
# Get advisor recommendation
|
220 |
-
advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
|
221 |
-
|
222 |
-
# Show advisor message
|
223 |
-
self.draft_output += format_agent_message(advisor, "USER", advice)
|
224 |
-
yield self.draft_output
|
225 |
-
|
226 |
-
self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
|
227 |
-
yield self.draft_output + "\n<!--USER_TURN-->"
|
228 |
-
return
|
229 |
-
else:
|
230 |
-
# A2A agent pick
|
231 |
-
messages = loop.run_until_complete(
|
232 |
-
self.run_a2a_draft_turn(team_num, round_num, pick_num)
|
233 |
-
)
|
234 |
-
|
235 |
-
# Display messages with typing effect
|
236 |
-
for msg in messages:
|
237 |
-
if len(msg) >= 3:
|
238 |
-
agent, recipient, content = msg[:3]
|
239 |
-
|
240 |
-
# Show "..." first for typing effect
|
241 |
-
typing_placeholder = format_agent_message(agent, recipient, "...")
|
242 |
-
self.draft_output += typing_placeholder
|
243 |
-
yield self.draft_output
|
244 |
-
time.sleep(TYPING_DELAY_SECONDS)
|
245 |
-
|
246 |
-
# Replace with actual message
|
247 |
-
self.draft_output = self.draft_output.replace(typing_placeholder, "")
|
248 |
-
self.draft_output += format_agent_message(agent, recipient, content)
|
249 |
-
yield self.draft_output
|
250 |
-
time.sleep(MESSAGE_DELAY_SECONDS)
|
251 |
-
|
252 |
-
time.sleep(TYPING_DELAY_SECONDS)
|
253 |
-
|
254 |
-
# End of round
|
255 |
-
self.draft_output += format_agent_message("commissioner", "ALL",
|
256 |
-
f"That's the end of Round {round_num}!")
|
257 |
-
yield self.draft_output
|
258 |
-
|
259 |
-
# Final summary
|
260 |
-
self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
|
261 |
-
self.draft_output += self.current_draft.get_draft_summary()
|
262 |
-
yield self.draft_output
|
263 |
-
|
264 |
-
# Clear the draft state
|
265 |
-
self.current_draft = None
|
266 |
-
|
267 |
-
async def run_a2a_draft_turn(self, team_num: int, round_num: int, pick_num: int):
|
268 |
-
"""Run a draft turn using A2A."""
|
269 |
-
messages = []
|
270 |
-
|
271 |
-
# Commissioner announcement
|
272 |
-
messages.append((
|
273 |
-
self.current_draft.commissioner,
|
274 |
-
"ALL",
|
275 |
-
f"Team {team_num} is on the clock!"
|
276 |
-
))
|
277 |
-
|
278 |
-
# Get available players
|
279 |
-
all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
|
280 |
-
available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
|
281 |
-
|
282 |
-
# Get pick from A2A agent
|
283 |
-
previous_picks = self.current_draft.draft_board.get(team_num, [])
|
284 |
-
pick_result = await self.a2a_manager.get_pick(team_num, available, previous_picks, round_num)
|
285 |
-
|
286 |
-
if not pick_result or pick_result.type != "pick":
|
287 |
-
# Fallback to simulation
|
288 |
-
messages.append((
|
289 |
-
self.current_draft.commissioner,
|
290 |
-
"ALL",
|
291 |
-
f"⚠️ Team {team_num} A2A agent not responding - using simulation"
|
292 |
-
))
|
293 |
-
|
294 |
-
sim_messages, _ = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
|
295 |
-
messages.extend(sim_messages)
|
296 |
-
return messages
|
297 |
-
|
298 |
-
# Make the pick
|
299 |
-
player = pick_result.player_name
|
300 |
-
self.current_draft.draft_board[team_num].append(player)
|
301 |
-
|
302 |
-
# Update agent's picks if it exists
|
303 |
-
agent = self.current_draft.agents.get(team_num)
|
304 |
-
if agent:
|
305 |
-
agent.picks.append(player)
|
306 |
-
|
307 |
-
# Commissioner announcement of pick
|
308 |
-
pick_num = len([p for picks in self.current_draft.draft_board.values() for p in picks])
|
309 |
-
confirm_msg = self.current_draft.commissioner.confirm_pick(
|
310 |
-
agent.team_name if agent else f"Team {team_num}",
|
311 |
-
player,
|
312 |
-
pick_num
|
313 |
-
)
|
314 |
-
messages.append((self.current_draft.commissioner, "ALL", confirm_msg))
|
315 |
-
|
316 |
-
# Agent explains reasoning
|
317 |
-
messages.append((
|
318 |
-
agent if agent else "system",
|
319 |
-
"ALL",
|
320 |
-
f"{pick_result.reasoning}"
|
321 |
-
))
|
322 |
-
|
323 |
-
if pick_result.trash_talk:
|
324 |
-
messages.append((
|
325 |
-
agent if agent else "system",
|
326 |
-
"ALL",
|
327 |
-
pick_result.trash_talk
|
328 |
-
))
|
329 |
-
|
330 |
-
# Get comments from other A2A agents
|
331 |
-
potential_commenters = [t for t in [1, 2, 3, 5, 6] if t != team_num and t != 4]
|
332 |
-
|
333 |
-
# Sort commenters to prioritize rivals
|
334 |
-
if team_num in RIVAL_PAIRS and potential_commenters:
|
335 |
-
rivals = RIVAL_PAIRS[team_num]
|
336 |
-
if isinstance(rivals, int):
|
337 |
-
rivals = [rivals]
|
338 |
-
# Put rivals first in the list
|
339 |
-
prioritized_commenters = [t for t in rivals if t in potential_commenters]
|
340 |
-
prioritized_commenters.extend([t for t in potential_commenters if t not in prioritized_commenters])
|
341 |
-
potential_commenters = prioritized_commenters
|
342 |
-
|
343 |
-
# Collect comments up to the configured limit
|
344 |
-
comment_count = 0
|
345 |
-
max_comments = self.a2a_manager.max_comments_per_pick
|
346 |
-
|
347 |
-
# Reduce comments if we're getting late in the draft
|
348 |
-
if pick_num >= 4: # After pick 4, reduce comments
|
349 |
-
max_comments = min(max_comments, 1)
|
350 |
-
|
351 |
-
for other_team in potential_commenters:
|
352 |
-
if comment_count >= max_comments:
|
353 |
-
break
|
354 |
-
|
355 |
-
comment = await self.a2a_manager.get_comment(other_team, team_num, player, round_num)
|
356 |
-
if comment:
|
357 |
-
other_agent = self.current_draft.agents.get(other_team)
|
358 |
-
if other_agent:
|
359 |
-
# Use the same pattern as earlier for the picking agent's name
|
360 |
-
picking_agent_name = agent.team_name if agent else f"Team {team_num}"
|
361 |
-
messages.append((
|
362 |
-
other_agent,
|
363 |
-
picking_agent_name,
|
364 |
-
comment
|
365 |
-
))
|
366 |
-
comment_count += 1
|
367 |
-
|
368 |
-
return messages
|
369 |
|
370 |
def continue_mock_draft(self, player_name: str):
|
371 |
"""Continue the mock draft after user makes a pick."""
|
@@ -402,26 +101,16 @@ class EnhancedFantasyDraftApp:
|
|
402 |
time.sleep(MESSAGE_DELAY_SECONDS)
|
403 |
|
404 |
# Continue with the rest of the draft
|
405 |
-
|
406 |
-
yield from self.continue_a2a_draft()
|
407 |
-
else:
|
408 |
-
yield from self.continue_basic_multiagent_draft()
|
409 |
|
410 |
-
def
|
411 |
-
"""Continue
|
412 |
# Calculate where we are
|
413 |
total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
|
414 |
current_round = ((total_picks - 1) // 6) + 1
|
415 |
|
416 |
-
#
|
417 |
-
|
418 |
-
loop = asyncio.get_running_loop()
|
419 |
-
except RuntimeError:
|
420 |
-
loop = asyncio.new_event_loop()
|
421 |
-
asyncio.set_event_loop(loop)
|
422 |
-
|
423 |
-
# Continue from current position
|
424 |
-
for round_num in range(current_round, 4):
|
425 |
if round_num > current_round:
|
426 |
self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
|
427 |
yield self.draft_output
|
@@ -432,11 +121,15 @@ class EnhancedFantasyDraftApp:
|
|
432 |
else:
|
433 |
pick_order = list(range(6, 0, -1))
|
434 |
|
435 |
-
# Calculate where we are in
|
436 |
picks_in_round = total_picks % 6
|
437 |
-
|
|
|
|
|
|
|
|
|
438 |
|
439 |
-
for pick_in_round, team_num in enumerate(
|
440 |
pick_num = (round_num - 1) * 6 + pick_in_round
|
441 |
|
442 |
# Show draft board at start of round
|
@@ -445,17 +138,21 @@ class EnhancedFantasyDraftApp:
|
|
445 |
self.draft_output += "\n"
|
446 |
yield self.draft_output
|
447 |
|
448 |
-
if team_num == 4: # User's turn
|
449 |
-
# Get advisor recommendation
|
450 |
advisor = self.current_draft.user_advisor
|
451 |
|
|
|
452 |
all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
|
453 |
available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
|
454 |
|
455 |
# Get other agent strategies for advisor context
|
456 |
strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
|
457 |
|
|
|
458 |
advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
|
|
|
|
|
459 |
self.draft_output += format_agent_message(advisor, "USER", advice)
|
460 |
yield self.draft_output
|
461 |
|
@@ -463,19 +160,21 @@ class EnhancedFantasyDraftApp:
|
|
463 |
yield self.draft_output + "\n<!--USER_TURN-->"
|
464 |
return
|
465 |
else:
|
466 |
-
#
|
467 |
-
messages =
|
468 |
-
self.run_a2a_draft_turn(team_num, round_num, pick_num)
|
469 |
-
)
|
470 |
|
|
|
471 |
for msg in messages:
|
472 |
if len(msg) >= 3:
|
473 |
agent, recipient, content = msg[:3]
|
|
|
|
|
474 |
typing_placeholder = format_agent_message(agent, recipient, "...")
|
475 |
self.draft_output += typing_placeholder
|
476 |
yield self.draft_output
|
477 |
time.sleep(TYPING_DELAY_SECONDS)
|
478 |
|
|
|
479 |
self.draft_output = self.draft_output.replace(typing_placeholder, "")
|
480 |
self.draft_output += format_agent_message(agent, recipient, content)
|
481 |
yield self.draft_output
|
@@ -483,6 +182,7 @@ class EnhancedFantasyDraftApp:
|
|
483 |
|
484 |
time.sleep(TYPING_DELAY_SECONDS)
|
485 |
|
|
|
486 |
self.draft_output += format_agent_message("commissioner", "ALL",
|
487 |
f"That's the end of Round {round_num}!")
|
488 |
yield self.draft_output
|
@@ -492,87 +192,12 @@ class EnhancedFantasyDraftApp:
|
|
492 |
self.draft_output += self.current_draft.get_draft_summary()
|
493 |
yield self.draft_output
|
494 |
|
495 |
-
|
496 |
-
|
497 |
-
def continue_basic_multiagent_draft(self):
|
498 |
-
"""Continue basic multiagent draft after user pick."""
|
499 |
-
# This is the original logic from app.py
|
500 |
-
total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
|
501 |
-
current_round = ((total_picks - 1) // 6) + 1
|
502 |
-
|
503 |
-
draft_memories = []
|
504 |
-
|
505 |
-
for round_num in range(current_round, 4):
|
506 |
-
if round_num > current_round:
|
507 |
-
self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
|
508 |
-
yield self.draft_output
|
509 |
-
|
510 |
-
if round_num % 2 == 1:
|
511 |
-
pick_order = list(range(1, 7))
|
512 |
-
else:
|
513 |
-
pick_order = list(range(6, 0, -1))
|
514 |
-
|
515 |
-
picks_in_round = total_picks % 6
|
516 |
-
start_idx = picks_in_round if round_num == current_round else 0
|
517 |
-
|
518 |
-
for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1):
|
519 |
-
pick_num = (round_num - 1) * 6 + pick_in_round
|
520 |
-
|
521 |
-
if pick_in_round == 1:
|
522 |
-
self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
|
523 |
-
self.draft_output += "\n"
|
524 |
-
yield self.draft_output
|
525 |
-
|
526 |
-
messages, result = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
|
527 |
-
|
528 |
-
for msg in messages:
|
529 |
-
if len(msg) >= 3:
|
530 |
-
agent, recipient, content = msg[:3]
|
531 |
-
|
532 |
-
if isinstance(agent, str) and agent.startswith("typing_"):
|
533 |
-
continue
|
534 |
-
else:
|
535 |
-
typing_placeholder = format_agent_message(agent, recipient, "...")
|
536 |
-
self.draft_output += typing_placeholder
|
537 |
-
yield self.draft_output
|
538 |
-
time.sleep(TYPING_DELAY_SECONDS)
|
539 |
-
|
540 |
-
self.draft_output = self.draft_output.replace(typing_placeholder, "")
|
541 |
-
self.draft_output += format_agent_message(agent, recipient, content)
|
542 |
-
yield self.draft_output
|
543 |
-
time.sleep(MESSAGE_DELAY_SECONDS)
|
544 |
-
|
545 |
-
if result is None:
|
546 |
-
self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
|
547 |
-
yield self.draft_output + "\n<!--USER_TURN-->"
|
548 |
-
return
|
549 |
-
|
550 |
-
if round_num > 1 and pick_in_round % 2 == 0:
|
551 |
-
if team_num in self.current_draft.agents:
|
552 |
-
agent = self.current_draft.agents[team_num]
|
553 |
-
if len(agent.picks) > 1:
|
554 |
-
memory = f"{agent.team_name} has drafted: {', '.join(agent.picks)}"
|
555 |
-
draft_memories.append(memory)
|
556 |
-
|
557 |
-
if draft_memories:
|
558 |
-
self.draft_output += format_memory_indicator(round_num, draft_memories[-2:])
|
559 |
-
yield self.draft_output
|
560 |
-
|
561 |
-
time.sleep(TYPING_DELAY_SECONDS)
|
562 |
-
|
563 |
-
self.draft_output += format_agent_message("commissioner", "ALL",
|
564 |
-
f"That's the end of Round {round_num}!")
|
565 |
-
yield self.draft_output
|
566 |
-
|
567 |
-
self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
|
568 |
-
self.draft_output += self.current_draft.get_draft_summary()
|
569 |
-
yield self.draft_output
|
570 |
-
|
571 |
self.current_draft = None
|
572 |
|
573 |
|
574 |
def create_gradio_interface():
|
575 |
-
"""Create the main Gradio interface
|
576 |
|
577 |
with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Soft()) as demo:
|
578 |
# Create state for each user session
|
@@ -588,25 +213,6 @@ def create_gradio_interface():
|
|
588 |
with gr.Tabs():
|
589 |
# Demo Tab
|
590 |
with gr.TabItem("🎮 Demo"):
|
591 |
-
# Add A2A Mode Toggle
|
592 |
-
with gr.Row():
|
593 |
-
with gr.Column():
|
594 |
-
gr.Markdown("### 🔧 Communication Mode")
|
595 |
-
communication_mode = gr.Radio(
|
596 |
-
["A2A", "Basic Multiagent"],
|
597 |
-
value="Basic Multiagent",
|
598 |
-
label="Select how agents communicate",
|
599 |
-
info="A2A: Distributed agents on HTTP servers | Basic Multiagent: Single-process execution"
|
600 |
-
)
|
601 |
-
mode_info = gr.Markdown(
|
602 |
-
"""
|
603 |
-
**Basic Multiagent** (Default): Fast single-process execution with reliable performance
|
604 |
-
**A2A**: Each agent runs on its own HTTP server with distributed architecture
|
605 |
-
|
606 |
-
*Basic Multiagent mode is recommended for Hugging Face Spaces deployments.*
|
607 |
-
"""
|
608 |
-
)
|
609 |
-
|
610 |
# Show agent cards
|
611 |
gr.Markdown("""
|
612 |
### 🏈 Meet Your Competition
|
@@ -636,7 +242,7 @@ def create_gradio_interface():
|
|
636 |
team1_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
637 |
with gr.Column(visible=False) as team1_prompt_col:
|
638 |
team1_prompt = gr.Textbox(
|
639 |
-
label="Team 1 Personality & Strategy
|
640 |
value="""You are Team 1, a fantasy football manager with Zero RB strategy.
|
641 |
|
642 |
PERSONALITY & STRATEGY:
|
@@ -655,7 +261,7 @@ Your EXTREME philosophy: RUNNING BACKS ARE DEAD TO ME! 💀 While others waste e
|
|
655 |
BE LOUD! BE PROUD! BE UNFORGETTABLE! 🎯""",
|
656 |
lines=15,
|
657 |
interactive=True,
|
658 |
-
info="Customize personality and strategy.
|
659 |
)
|
660 |
team1_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
661 |
|
@@ -676,7 +282,7 @@ BE LOUD! BE PROUD! BE UNFORGETTABLE! 🎯""",
|
|
676 |
team2_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
677 |
with gr.Column(visible=False) as team2_prompt_col:
|
678 |
team2_prompt = gr.Textbox(
|
679 |
-
label="Team 2 Personality & Strategy
|
680 |
value="""You are Team 2, a fantasy football manager with BPA (Best Player Available) strategy.
|
681 |
|
682 |
PERSONALITY & STRATEGY:
|
@@ -694,7 +300,7 @@ Your EXTREME philosophy: PROCESS OVER EVERYTHING! 📊 I don't care about your "
|
|
694 |
BE RUTHLESS! BE RIGHT! BE THE VALUE VULTURE! 🦅""",
|
695 |
lines=15,
|
696 |
interactive=True,
|
697 |
-
info="Customize personality and strategy.
|
698 |
)
|
699 |
team2_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
700 |
|
@@ -715,7 +321,7 @@ BE RUTHLESS! BE RIGHT! BE THE VALUE VULTURE! 🦅""",
|
|
715 |
team3_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
716 |
with gr.Column(visible=False) as team3_prompt_col:
|
717 |
team3_prompt = gr.Textbox(
|
718 |
-
label="Team 3 Personality & Strategy
|
719 |
value="""You are Team 3, a fantasy football manager with Robust RB strategy.
|
720 |
|
721 |
PERSONALITY & STRATEGY:
|
@@ -733,7 +339,7 @@ Your EXTREME philosophy: GROUND AND POUND FOREVER! 🏃♂️ These young pun
|
|
733 |
BE STUBBORN! BE TRADITIONAL! ESTABLISH THE RUN! 🏈""",
|
734 |
lines=15,
|
735 |
interactive=True,
|
736 |
-
info="Customize personality and strategy.
|
737 |
)
|
738 |
team3_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
739 |
|
@@ -768,7 +374,7 @@ BE STUBBORN! BE TRADITIONAL! ESTABLISH THE RUN! 🏈""",
|
|
768 |
team5_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
769 |
with gr.Column(visible=False) as team5_prompt_col:
|
770 |
team5_prompt = gr.Textbox(
|
771 |
-
label="Team 5 Personality & Strategy
|
772 |
value="""You are Team 5, a fantasy football manager with Upside Hunter strategy.
|
773 |
|
774 |
PERSONALITY & STRATEGY:
|
@@ -786,7 +392,7 @@ Your EXTREME philosophy: BOOM OR BUST, BABY! 💥 Why settle for consistent medi
|
|
786 |
BE BOLD! BE RECKLESS! SWING FOR THE FENCES! ⚡""",
|
787 |
lines=15,
|
788 |
interactive=True,
|
789 |
-
info="Customize personality and strategy.
|
790 |
)
|
791 |
team5_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
792 |
|
@@ -807,7 +413,7 @@ BE BOLD! BE RECKLESS! SWING FOR THE FENCES! ⚡""",
|
|
807 |
team6_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
808 |
with gr.Column(visible=False) as team6_prompt_col:
|
809 |
team6_prompt = gr.Textbox(
|
810 |
-
label="Team 6 Personality & Strategy
|
811 |
value="""You are Team 6, a fantasy football manager with BPA (Best Player Available) strategy.
|
812 |
|
813 |
PERSONALITY & STRATEGY:
|
@@ -825,7 +431,7 @@ Your EXTREME philosophy: THE SPREADSHEET NEVER LIES! 📈 I have SEVENTEEN model
|
|
825 |
BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
826 |
lines=15,
|
827 |
interactive=True,
|
828 |
-
info="Customize personality and strategy.
|
829 |
)
|
830 |
team6_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
831 |
|
@@ -863,40 +469,6 @@ BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
|
863 |
interactive=False
|
864 |
)
|
865 |
|
866 |
-
# Debug Tab
|
867 |
-
with gr.TabItem("🔍 Debug"):
|
868 |
-
gr.Markdown("""
|
869 |
-
## A2A Debugging Tools
|
870 |
-
|
871 |
-
Use these tools to test A2A functionality and diagnose any issues.
|
872 |
-
""")
|
873 |
-
|
874 |
-
with gr.Row():
|
875 |
-
with gr.Column():
|
876 |
-
test_a2a_btn = gr.Button("🧪 Test A2A Dependencies & Ports", variant="primary", size="lg")
|
877 |
-
test_network_btn = gr.Button("🌐 Test Network Connectivity", variant="secondary", size="lg")
|
878 |
-
|
879 |
-
a2a_test_output = gr.Textbox(
|
880 |
-
label="Test Results",
|
881 |
-
lines=20,
|
882 |
-
interactive=False,
|
883 |
-
elem_classes=["monospace"]
|
884 |
-
)
|
885 |
-
|
886 |
-
gr.Markdown("""
|
887 |
-
### What this test checks:
|
888 |
-
- ✓ Python environment and version
|
889 |
-
- ✓ a2a-sdk package installation
|
890 |
-
- ✓ Module imports (a2a, any_agent)
|
891 |
-
- ✓ Port availability for agents
|
892 |
-
- ✓ A2A agent startup capability
|
893 |
-
|
894 |
-
### Common issues:
|
895 |
-
- **Import errors**: Check if a2a-sdk is properly installed
|
896 |
-
- **Port conflicts**: Other services might be using ports 5000-9000
|
897 |
-
- **Timeout errors**: Network latency on cloud deployments
|
898 |
-
""")
|
899 |
-
|
900 |
# How It Works Tab
|
901 |
with gr.TabItem("🔧 How It Works"):
|
902 |
gr.Markdown("""
|
@@ -919,28 +491,15 @@ BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
|
919 |
- **Strategy Memory**: Remembers own strategy and others' approaches
|
920 |
- **Pick History**: Tracks all selections for informed decisions
|
921 |
|
922 |
-
### 💬
|
923 |
|
924 |
-
**
|
925 |
-
|
926 |
-
|
927 |
-
- **
|
928 |
-
- **Dynamic Ports**: Each session gets unique ports automatically (5000-9000 range)
|
929 |
-
- **True Isolation**: No shared memory, HTTP communication only
|
930 |
-
- **Production Ready**: Scalable to multiple machines
|
931 |
-
- **Real HTTP Calls**: Agents communicate via actual network requests
|
932 |
-
- **Task Continuity**: Conversation context maintained across turns
|
933 |
-
|
934 |
-
#### 2. Basic Multiagent Mode
|
935 |
-
- Single process, direct method calls
|
936 |
-
- Shared memory between agents
|
937 |
-
- Fast execution, simple debugging
|
938 |
-
- Fallback option if A2A requirements aren't met
|
939 |
|
940 |
### 📊 Architecture Flow
|
941 |
-
""")
|
942 |
|
943 |
-
gr.Markdown("""
|
944 |
#### 1️⃣ INITIALIZATION
|
945 |
User clicks "Start Mock Draft" → System creates 6 agents
|
946 |
|
@@ -959,7 +518,7 @@ BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
|
959 |
|
960 |
#### 4️⃣ EACH PICK TRIGGERS
|
961 |
- Agent makes selection based on strategy
|
962 |
-
- Other agents comment
|
963 |
- Original agent may respond
|
964 |
- All agents update their memory
|
965 |
|
@@ -1009,376 +568,161 @@ BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
|
1009 |
"" # Clear the input
|
1010 |
)
|
1011 |
|
1012 |
-
#
|
1013 |
-
def test_a2a_functionality():
|
1014 |
-
"""Test A2A dependencies and port availability."""
|
1015 |
-
import socket
|
1016 |
-
import subprocess
|
1017 |
-
import importlib.util
|
1018 |
-
import site
|
1019 |
-
|
1020 |
-
test_results = []
|
1021 |
-
|
1022 |
-
# 1. Python Environment
|
1023 |
-
test_results.append("=== Python Environment ===")
|
1024 |
-
test_results.append(f"Python: {sys.version.split()[0]}")
|
1025 |
-
test_results.append(f"Platform: {sys.platform}")
|
1026 |
-
test_results.append(f"SPACE_ID: {os.getenv('SPACE_ID', 'Not on HF Spaces')}")
|
1027 |
-
|
1028 |
-
# 2. Check a2a-sdk installation
|
1029 |
-
test_results.append("\n=== Package Installation ===")
|
1030 |
-
try:
|
1031 |
-
result = subprocess.run([sys.executable, "-m", "pip", "show", "a2a-sdk"],
|
1032 |
-
capture_output=True, text=True, timeout=5)
|
1033 |
-
if result.returncode == 0:
|
1034 |
-
version_line = [line for line in result.stdout.split('\n') if line.startswith('Version:')]
|
1035 |
-
location_line = [line for line in result.stdout.split('\n') if line.startswith('Location:')]
|
1036 |
-
test_results.append(f"✅ a2a-sdk installed: {version_line[0] if version_line else 'Unknown version'}")
|
1037 |
-
if location_line:
|
1038 |
-
test_results.append(f" {location_line[0]}")
|
1039 |
-
else:
|
1040 |
-
test_results.append("❌ a2a-sdk NOT installed according to pip")
|
1041 |
-
except Exception as e:
|
1042 |
-
test_results.append(f"❌ Error checking pip: {e}")
|
1043 |
-
|
1044 |
-
# 3. Module search
|
1045 |
-
test_results.append("\n=== Module Search ===")
|
1046 |
-
a2a_spec = importlib.util.find_spec("a2a")
|
1047 |
-
if a2a_spec:
|
1048 |
-
test_results.append(f"✅ a2a module found at: {a2a_spec.origin}")
|
1049 |
-
else:
|
1050 |
-
test_results.append("❌ a2a module NOT found by importlib")
|
1051 |
-
# Manual search
|
1052 |
-
for path in site.getsitepackages():
|
1053 |
-
if os.path.exists(path):
|
1054 |
-
a2a_path = os.path.join(path, "a2a")
|
1055 |
-
if os.path.exists(a2a_path):
|
1056 |
-
test_results.append(f" Found a2a directory at: {a2a_path}")
|
1057 |
-
|
1058 |
-
# 4. Import tests
|
1059 |
-
test_results.append("\n=== Import Tests ===")
|
1060 |
-
|
1061 |
-
# Basic a2a import
|
1062 |
-
try:
|
1063 |
-
import a2a
|
1064 |
-
test_results.append(f"✅ import a2a: Success")
|
1065 |
-
try:
|
1066 |
-
import a2a.types
|
1067 |
-
test_results.append("✅ import a2a.types: Success")
|
1068 |
-
try:
|
1069 |
-
from a2a.types import AgentSkill
|
1070 |
-
test_results.append("✅ from a2a.types import AgentSkill: Success")
|
1071 |
-
except ImportError as e:
|
1072 |
-
test_results.append(f"❌ AgentSkill import: {e}")
|
1073 |
-
except ImportError as e:
|
1074 |
-
test_results.append(f"❌ a2a.types import: {e}")
|
1075 |
-
except ImportError as e:
|
1076 |
-
test_results.append(f"❌ a2a import failed: {e}")
|
1077 |
-
|
1078 |
-
# any_agent A2A imports
|
1079 |
-
try:
|
1080 |
-
from any_agent.serving import A2AServingConfig
|
1081 |
-
from any_agent.tools import a2a_tool_async
|
1082 |
-
test_results.append("✅ any_agent A2A components: Success!")
|
1083 |
-
except ImportError as e:
|
1084 |
-
test_results.append(f"❌ any_agent A2A import: {e}")
|
1085 |
-
|
1086 |
-
# 5. Port availability
|
1087 |
-
test_results.append("\n=== Port Availability ===")
|
1088 |
-
test_ports = [5001, 5002, 5003, 5004, 5005, 5006]
|
1089 |
-
available_count = 0
|
1090 |
-
|
1091 |
-
for port in test_ports:
|
1092 |
-
try:
|
1093 |
-
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
1094 |
-
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
1095 |
-
sock.bind(('127.0.0.1', port))
|
1096 |
-
test_results.append(f"✅ Port {port} available")
|
1097 |
-
available_count += 1
|
1098 |
-
sock.close()
|
1099 |
-
except Exception:
|
1100 |
-
test_results.append(f"❌ Port {port} not available")
|
1101 |
-
|
1102 |
-
test_results.append(f"\n📊 Summary: {available_count}/{len(test_ports)} ports available")
|
1103 |
-
|
1104 |
-
# 6. Check any-agent version if imports failed
|
1105 |
-
if "❌ any_agent A2A import" in "\n".join(test_results):
|
1106 |
-
test_results.append("\n=== Version Check ===")
|
1107 |
-
try:
|
1108 |
-
result = subprocess.run([sys.executable, "-m", "pip", "show", "any-agent"],
|
1109 |
-
capture_output=True, text=True, timeout=5)
|
1110 |
-
if result.returncode == 0:
|
1111 |
-
version_line = [line for line in result.stdout.split('\n') if line.startswith('Version:')]
|
1112 |
-
if version_line:
|
1113 |
-
test_results.append(f"any-agent version: {version_line[0]}")
|
1114 |
-
# Check if version is < 0.22
|
1115 |
-
import re
|
1116 |
-
version_match = re.search(r'Version: (\d+)\.(\d+)', version_line[0])
|
1117 |
-
if version_match:
|
1118 |
-
major, minor = int(version_match.group(1)), int(version_match.group(2))
|
1119 |
-
if major == 0 and minor < 22:
|
1120 |
-
test_results.append("⚠️ any-agent version < 0.22.0 - A2A components not available")
|
1121 |
-
test_results.append("💡 Solution: Update requirements.txt to any-agent[a2a,openai]>=0.22.0")
|
1122 |
-
except Exception as e:
|
1123 |
-
test_results.append(f"Could not check any-agent version: {e}")
|
1124 |
-
|
1125 |
-
# Final verdict
|
1126 |
-
if available_count >= 6 and "✅ any_agent A2A components: Success!" in "\n".join(test_results):
|
1127 |
-
test_results.append("\n✅ A2A should work! Try selecting A2A mode.")
|
1128 |
-
else:
|
1129 |
-
test_results.append("\n❌ A2A requirements not met. Use Basic Multiagent mode.")
|
1130 |
-
|
1131 |
-
return "\n".join(test_results)
|
1132 |
-
|
1133 |
-
# Test network connectivity for A2A
|
1134 |
-
async def test_network_connectivity():
|
1135 |
-
"""Test network connectivity for A2A servers."""
|
1136 |
-
import httpx
|
1137 |
-
|
1138 |
-
results = []
|
1139 |
-
results.append("=== A2A Network Connectivity Test ===")
|
1140 |
-
results.append(f"Running on HF Spaces: {bool(os.getenv('SPACE_ID'))}\n")
|
1141 |
-
|
1142 |
-
# Test different host configurations
|
1143 |
-
configs = [
|
1144 |
-
("localhost", 8000, "Local loopback"),
|
1145 |
-
("127.0.0.1", 8000, "IP loopback"),
|
1146 |
-
("0.0.0.0", 8000, "All interfaces"),
|
1147 |
-
]
|
1148 |
-
|
1149 |
-
for host, port, desc in configs:
|
1150 |
-
results.append(f"Testing {host}:{port} ({desc})...")
|
1151 |
-
|
1152 |
-
timeout = httpx.Timeout(timeout=5.0, connect=2.0, read=5.0)
|
1153 |
-
|
1154 |
-
async with httpx.AsyncClient(timeout=timeout) as client:
|
1155 |
-
# Test basic connectivity
|
1156 |
-
try:
|
1157 |
-
response = await client.get(f"http://{host}:{port}/")
|
1158 |
-
results.append(f" ✅ Connected: HTTP {response.status_code}")
|
1159 |
-
except httpx.ConnectError:
|
1160 |
-
results.append(f" ❌ Connection refused (no server)")
|
1161 |
-
except httpx.ReadTimeout:
|
1162 |
-
results.append(f" ❌ Read timeout (server not responding)")
|
1163 |
-
except Exception as e:
|
1164 |
-
results.append(f" ❌ Error: {type(e).__name__}")
|
1165 |
-
|
1166 |
-
results.append("")
|
1167 |
-
|
1168 |
-
results.append("=== Recommendations ===")
|
1169 |
-
if os.getenv("SPACE_ID"):
|
1170 |
-
results.append("On HF Spaces:")
|
1171 |
-
results.append("• A2A servers bind to 0.0.0.0 for external access")
|
1172 |
-
results.append("• Clients connect via 127.0.0.1 internally")
|
1173 |
-
results.append("• Network timeouts are common - retries help")
|
1174 |
-
results.append("• Consider Basic Multiagent mode for reliability")
|
1175 |
-
else:
|
1176 |
-
results.append("Local development:")
|
1177 |
-
results.append("• Both localhost and 127.0.0.1 should work")
|
1178 |
-
results.append("• Check firewall if connections fail")
|
1179 |
-
|
1180 |
-
return "\n".join(results)
|
1181 |
-
|
1182 |
-
# No need for separate mode change handler - it happens when draft starts
|
1183 |
-
|
1184 |
-
# Functions to handle prompt editing
|
1185 |
def toggle_prompt_visibility():
|
1186 |
-
"""Toggle prompt editor visibility."""
|
1187 |
return gr.update(visible=True)
|
1188 |
|
|
|
1189 |
def save_prompt(team_num, prompt_text, app, prompts_dict):
|
1190 |
-
"""Save custom prompt for a team."""
|
1191 |
if app is None:
|
1192 |
-
app =
|
1193 |
-
if prompts_dict is None:
|
1194 |
-
prompts_dict = {}
|
1195 |
-
|
1196 |
prompts_dict[team_num] = prompt_text
|
1197 |
-
app.custom_prompts
|
1198 |
return app, prompts_dict, gr.update(visible=False)
|
1199 |
|
1200 |
-
# Run
|
1201 |
-
def run_and_check(
|
1202 |
-
"""Run
|
1203 |
-
# Create a new app instance for this user if needed
|
1204 |
if app is None:
|
1205 |
-
app =
|
1206 |
-
|
1207 |
-
# Apply custom prompts if any
|
1208 |
-
if prompts_dict:
|
1209 |
app.custom_prompts = prompts_dict
|
1210 |
|
1211 |
-
|
1212 |
-
|
1213 |
-
|
1214 |
-
|
1215 |
-
|
1216 |
-
|
1217 |
-
|
1218 |
-
|
1219 |
-
|
1220 |
-
|
1221 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1222 |
|
1223 |
-
#
|
1224 |
team1_save_btn.click(
|
1225 |
-
lambda
|
1226 |
-
[team1_prompt, app_state, agent_prompts],
|
1227 |
-
[app_state, agent_prompts, team1_prompt_col]
|
1228 |
)
|
1229 |
team2_save_btn.click(
|
1230 |
-
lambda
|
1231 |
-
[team2_prompt, app_state, agent_prompts],
|
1232 |
-
[app_state, agent_prompts, team2_prompt_col]
|
1233 |
)
|
1234 |
team3_save_btn.click(
|
1235 |
-
lambda
|
1236 |
-
[team3_prompt, app_state, agent_prompts],
|
1237 |
-
[app_state, agent_prompts, team3_prompt_col]
|
1238 |
)
|
1239 |
team5_save_btn.click(
|
1240 |
-
lambda
|
1241 |
-
[team5_prompt, app_state, agent_prompts],
|
1242 |
-
[app_state, agent_prompts, team5_prompt_col]
|
1243 |
)
|
1244 |
team6_save_btn.click(
|
1245 |
-
lambda
|
1246 |
-
[team6_prompt, app_state, agent_prompts],
|
1247 |
-
[app_state, agent_prompts, team6_prompt_col]
|
1248 |
)
|
1249 |
|
|
|
1250 |
run_multiagent_btn.click(
|
1251 |
-
run_and_check,
|
1252 |
-
[
|
1253 |
-
[multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input
|
1254 |
-
show_progress=True
|
1255 |
-
)
|
1256 |
-
|
1257 |
-
# Wrapper for async network test
|
1258 |
-
def run_network_test():
|
1259 |
-
"""Run the async network test."""
|
1260 |
-
loop = asyncio.new_event_loop()
|
1261 |
-
asyncio.set_event_loop(loop)
|
1262 |
-
return loop.run_until_complete(test_network_connectivity())
|
1263 |
-
|
1264 |
-
# Wire up test buttons
|
1265 |
-
test_a2a_btn.click(
|
1266 |
-
test_a2a_functionality,
|
1267 |
-
[],
|
1268 |
-
[a2a_test_output]
|
1269 |
-
)
|
1270 |
-
|
1271 |
-
test_network_btn.click(
|
1272 |
-
run_network_test,
|
1273 |
-
[],
|
1274 |
-
[a2a_test_output]
|
1275 |
)
|
1276 |
|
1277 |
-
#
|
1278 |
-
def submit_and_continue(player_name, app):
|
1279 |
-
"""Submit pick and continue draft."""
|
1280 |
-
if app is None:
|
1281 |
-
yield ("No active draft. Please start a new mock draft.",
|
1282 |
-
gr.update(visible=False), gr.update(visible=False), "", "", None)
|
1283 |
-
return
|
1284 |
-
|
1285 |
-
for output in app.continue_mock_draft(player_name):
|
1286 |
-
result = check_user_turn(output, app)
|
1287 |
-
yield result + (app,) # Return the app state as the last element
|
1288 |
-
|
1289 |
submit_pick_btn.click(
|
1290 |
submit_and_continue,
|
1291 |
-
[draft_pick_input, app_state],
|
1292 |
-
[multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input
|
1293 |
-
show_progress=True
|
1294 |
)
|
1295 |
|
1296 |
-
#
|
1297 |
draft_pick_input.submit(
|
1298 |
submit_and_continue,
|
1299 |
-
[draft_pick_input, app_state],
|
1300 |
-
[multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input
|
1301 |
-
show_progress=True
|
1302 |
)
|
1303 |
|
1304 |
-
#
|
1305 |
demo.css = """
|
1306 |
#main-container {
|
1307 |
-
max-width:
|
1308 |
margin: 0 auto;
|
1309 |
}
|
1310 |
|
1311 |
.multiagent-output {
|
1312 |
-
max-height:
|
1313 |
overflow-y: auto;
|
|
|
|
|
|
|
1314 |
}
|
1315 |
|
1316 |
-
|
1317 |
-
|
1318 |
-
|
1319 |
-
}
|
1320 |
-
|
1321 |
-
.multiagent-output div[style*="background-color"] * {
|
1322 |
-
color: #212121 !important;
|
1323 |
}
|
1324 |
|
1325 |
#start-button {
|
1326 |
-
|
|
|
1327 |
}
|
1328 |
|
1329 |
-
|
1330 |
-
|
1331 |
-
font-family: 'Courier New', Courier, monospace;
|
1332 |
-
font-size: 12px;
|
1333 |
-
}
|
1334 |
-
|
1335 |
-
/* Settings button styling */
|
1336 |
-
button[variant="secondary"] {
|
1337 |
-
margin-top: 8px;
|
1338 |
-
width: 100%;
|
1339 |
-
}
|
1340 |
-
|
1341 |
-
/* Prompt editor styling */
|
1342 |
-
.prompt-editor {
|
1343 |
-
margin-top: 10px;
|
1344 |
-
padding: 10px;
|
1345 |
-
background-color: #f5f5f5;
|
1346 |
-
border-radius: 4px;
|
1347 |
}
|
1348 |
"""
|
1349 |
-
|
1350 |
-
# Note: Gradio's unload() doesn't support inputs, so automatic cleanup
|
1351 |
-
# happens when the Python process ends or when new sessions override old ones
|
1352 |
|
1353 |
return demo
|
1354 |
|
1355 |
|
1356 |
def main():
|
1357 |
-
"""
|
1358 |
-
#
|
1359 |
-
if
|
1360 |
-
|
1361 |
-
|
1362 |
-
|
1363 |
-
print("Error: OPENAI_API_KEY not found in environment")
|
1364 |
-
print("Please set it using: export OPENAI_API_KEY='your-key-here'")
|
1365 |
-
exit(1)
|
1366 |
|
1367 |
# Create and launch the interface
|
1368 |
demo = create_gradio_interface()
|
1369 |
|
1370 |
-
|
1371 |
-
|
1372 |
-
|
1373 |
-
|
1374 |
-
|
1375 |
-
|
1376 |
-
demo.launch(
|
1377 |
-
server_name="0.0.0.0",
|
1378 |
-
server_port=7860,
|
1379 |
-
share=True,
|
1380 |
-
show_error=True
|
1381 |
-
)
|
1382 |
|
1383 |
|
1384 |
if __name__ == "__main__":
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
+
Fantasy Draft Multi-Agent Demo
|
4 |
+
Multi-agent system using the any-agent framework for fantasy football drafts
|
5 |
"""
|
6 |
|
7 |
import os
|
|
|
12 |
from typing import List, Tuple, Optional, Dict
|
13 |
from dotenv import load_dotenv
|
14 |
import sys
|
15 |
+
|
16 |
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
17 |
|
18 |
from core.agent import FantasyDraftAgent
|
|
|
20 |
from core.constants import (
|
21 |
TYPING_DELAY_SECONDS,
|
22 |
MESSAGE_DELAY_SECONDS,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
)
|
|
|
|
|
|
|
24 |
|
25 |
from apps.multiagent_draft import MultiAgentMockDraft
|
26 |
from apps.multiagent_scenarios import (
|
|
|
31 |
create_mock_draft_visualization
|
32 |
)
|
33 |
|
|
|
|
|
|
|
34 |
# Apply nest_asyncio for async in Gradio
|
35 |
nest_asyncio.apply()
|
36 |
|
|
|
41 |
load_dotenv()
|
42 |
|
43 |
|
44 |
+
class FantasyDraftApp:
|
45 |
def __init__(self):
|
46 |
self.current_draft = None # Store the current mock draft
|
47 |
self.draft_output = "" # Store the draft output so far
|
|
|
|
|
|
|
|
|
48 |
self.custom_prompts = {} # Store custom agent prompts
|
49 |
|
50 |
+
def run_multiagent_demo(self):
|
51 |
+
"""Run the mock draft demonstration."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
# Reset any previous draft
|
53 |
self.current_draft = None
|
54 |
self.draft_output = ""
|
55 |
|
56 |
+
# Use basic multiagent draft
|
57 |
+
draft_generator = run_interactive_mock_draft()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
+
for output in draft_generator:
|
60 |
+
if isinstance(output, tuple):
|
61 |
+
# This means it's the user's turn
|
62 |
+
self.current_draft, self.draft_output = output
|
63 |
+
yield self.draft_output + "\n<!--USER_TURN-->"
|
64 |
+
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
else:
|
66 |
+
self.draft_output = output
|
67 |
+
yield output
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
68 |
|
69 |
def continue_mock_draft(self, player_name: str):
|
70 |
"""Continue the mock draft after user makes a pick."""
|
|
|
101 |
time.sleep(MESSAGE_DELAY_SECONDS)
|
102 |
|
103 |
# Continue with the rest of the draft
|
104 |
+
yield from self.continue_basic_multiagent_draft()
|
|
|
|
|
|
|
105 |
|
106 |
+
def continue_basic_multiagent_draft(self):
|
107 |
+
"""Continue basic multiagent draft after user pick."""
|
108 |
# Calculate where we are
|
109 |
total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
|
110 |
current_round = ((total_picks - 1) // 6) + 1
|
111 |
|
112 |
+
# Continue from where we left off
|
113 |
+
for round_num in range(current_round, 4): # Continue through round 3
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
if round_num > current_round:
|
115 |
self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
|
116 |
yield self.draft_output
|
|
|
121 |
else:
|
122 |
pick_order = list(range(6, 0, -1))
|
123 |
|
124 |
+
# Calculate where we are in this round
|
125 |
picks_in_round = total_picks % 6
|
126 |
+
if round_num == current_round:
|
127 |
+
# Skip picks already made
|
128 |
+
start_idx = picks_in_round
|
129 |
+
else:
|
130 |
+
start_idx = 0
|
131 |
|
132 |
+
for pick_in_round, team_num in enumerate(pick_order[start_idx:], start_idx + 1):
|
133 |
pick_num = (round_num - 1) * 6 + pick_in_round
|
134 |
|
135 |
# Show draft board at start of round
|
|
|
138 |
self.draft_output += "\n"
|
139 |
yield self.draft_output
|
140 |
|
141 |
+
if team_num == 4: # User's turn
|
142 |
+
# Get advisor recommendation
|
143 |
advisor = self.current_draft.user_advisor
|
144 |
|
145 |
+
# Get available players
|
146 |
all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
|
147 |
available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
|
148 |
|
149 |
# Get other agent strategies for advisor context
|
150 |
strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
|
151 |
|
152 |
+
# Get advisor recommendation
|
153 |
advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
|
154 |
+
|
155 |
+
# Show advisor message
|
156 |
self.draft_output += format_agent_message(advisor, "USER", advice)
|
157 |
yield self.draft_output
|
158 |
|
|
|
160 |
yield self.draft_output + "\n<!--USER_TURN-->"
|
161 |
return
|
162 |
else:
|
163 |
+
# AI agent pick
|
164 |
+
messages, _ = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
|
|
|
|
|
165 |
|
166 |
+
# Display messages with typing effect
|
167 |
for msg in messages:
|
168 |
if len(msg) >= 3:
|
169 |
agent, recipient, content = msg[:3]
|
170 |
+
|
171 |
+
# Show "..." first for typing effect
|
172 |
typing_placeholder = format_agent_message(agent, recipient, "...")
|
173 |
self.draft_output += typing_placeholder
|
174 |
yield self.draft_output
|
175 |
time.sleep(TYPING_DELAY_SECONDS)
|
176 |
|
177 |
+
# Replace with actual message
|
178 |
self.draft_output = self.draft_output.replace(typing_placeholder, "")
|
179 |
self.draft_output += format_agent_message(agent, recipient, content)
|
180 |
yield self.draft_output
|
|
|
182 |
|
183 |
time.sleep(TYPING_DELAY_SECONDS)
|
184 |
|
185 |
+
# End of round
|
186 |
self.draft_output += format_agent_message("commissioner", "ALL",
|
187 |
f"That's the end of Round {round_num}!")
|
188 |
yield self.draft_output
|
|
|
192 |
self.draft_output += self.current_draft.get_draft_summary()
|
193 |
yield self.draft_output
|
194 |
|
195 |
+
# Clear the draft state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
196 |
self.current_draft = None
|
197 |
|
198 |
|
199 |
def create_gradio_interface():
|
200 |
+
"""Create the main Gradio interface."""
|
201 |
|
202 |
with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Soft()) as demo:
|
203 |
# Create state for each user session
|
|
|
213 |
with gr.Tabs():
|
214 |
# Demo Tab
|
215 |
with gr.TabItem("🎮 Demo"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
216 |
# Show agent cards
|
217 |
gr.Markdown("""
|
218 |
### 🏈 Meet Your Competition
|
|
|
242 |
team1_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
243 |
with gr.Column(visible=False) as team1_prompt_col:
|
244 |
team1_prompt = gr.Textbox(
|
245 |
+
label="Team 1 Personality & Strategy",
|
246 |
value="""You are Team 1, a fantasy football manager with Zero RB strategy.
|
247 |
|
248 |
PERSONALITY & STRATEGY:
|
|
|
261 |
BE LOUD! BE PROUD! BE UNFORGETTABLE! 🎯""",
|
262 |
lines=15,
|
263 |
interactive=True,
|
264 |
+
info="Customize personality and strategy."
|
265 |
)
|
266 |
team1_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
267 |
|
|
|
282 |
team2_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
283 |
with gr.Column(visible=False) as team2_prompt_col:
|
284 |
team2_prompt = gr.Textbox(
|
285 |
+
label="Team 2 Personality & Strategy",
|
286 |
value="""You are Team 2, a fantasy football manager with BPA (Best Player Available) strategy.
|
287 |
|
288 |
PERSONALITY & STRATEGY:
|
|
|
300 |
BE RUTHLESS! BE RIGHT! BE THE VALUE VULTURE! 🦅""",
|
301 |
lines=15,
|
302 |
interactive=True,
|
303 |
+
info="Customize personality and strategy."
|
304 |
)
|
305 |
team2_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
306 |
|
|
|
321 |
team3_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
322 |
with gr.Column(visible=False) as team3_prompt_col:
|
323 |
team3_prompt = gr.Textbox(
|
324 |
+
label="Team 3 Personality & Strategy",
|
325 |
value="""You are Team 3, a fantasy football manager with Robust RB strategy.
|
326 |
|
327 |
PERSONALITY & STRATEGY:
|
|
|
339 |
BE STUBBORN! BE TRADITIONAL! ESTABLISH THE RUN! 🏈""",
|
340 |
lines=15,
|
341 |
interactive=True,
|
342 |
+
info="Customize personality and strategy."
|
343 |
)
|
344 |
team3_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
345 |
|
|
|
374 |
team5_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
375 |
with gr.Column(visible=False) as team5_prompt_col:
|
376 |
team5_prompt = gr.Textbox(
|
377 |
+
label="Team 5 Personality & Strategy",
|
378 |
value="""You are Team 5, a fantasy football manager with Upside Hunter strategy.
|
379 |
|
380 |
PERSONALITY & STRATEGY:
|
|
|
392 |
BE BOLD! BE RECKLESS! SWING FOR THE FENCES! ⚡""",
|
393 |
lines=15,
|
394 |
interactive=True,
|
395 |
+
info="Customize personality and strategy."
|
396 |
)
|
397 |
team5_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
398 |
|
|
|
413 |
team6_settings_btn = gr.Button("⚙️ Customize", size="sm", variant="secondary")
|
414 |
with gr.Column(visible=False) as team6_prompt_col:
|
415 |
team6_prompt = gr.Textbox(
|
416 |
+
label="Team 6 Personality & Strategy",
|
417 |
value="""You are Team 6, a fantasy football manager with BPA (Best Player Available) strategy.
|
418 |
|
419 |
PERSONALITY & STRATEGY:
|
|
|
431 |
BE ANALYTICAL! BE MERCILESS! TRUST THE PROCESS! 🤖""",
|
432 |
lines=15,
|
433 |
interactive=True,
|
434 |
+
info="Customize personality and strategy."
|
435 |
)
|
436 |
team6_save_btn = gr.Button("💾 Save", size="sm", variant="primary")
|
437 |
|
|
|
469 |
interactive=False
|
470 |
)
|
471 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
472 |
# How It Works Tab
|
473 |
with gr.TabItem("🔧 How It Works"):
|
474 |
gr.Markdown("""
|
|
|
491 |
- **Strategy Memory**: Remembers own strategy and others' approaches
|
492 |
- **Pick History**: Tracks all selections for informed decisions
|
493 |
|
494 |
+
### 💬 Single-Process Multi-Agent Communication
|
495 |
|
496 |
+
- **In-Memory Communication**: Agents interact directly via method calls
|
497 |
+
- **Shared Draft State**: All agents see the same draft board
|
498 |
+
- **Fast Execution**: No network overhead
|
499 |
+
- **Conversation Memory**: Each agent remembers interactions
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
|
501 |
### 📊 Architecture Flow
|
|
|
502 |
|
|
|
503 |
#### 1️⃣ INITIALIZATION
|
504 |
User clicks "Start Mock Draft" → System creates 6 agents
|
505 |
|
|
|
518 |
|
519 |
#### 4️⃣ EACH PICK TRIGGERS
|
520 |
- Agent makes selection based on strategy
|
521 |
+
- Other agents comment based on rivalries
|
522 |
- Original agent may respond
|
523 |
- All agents update their memory
|
524 |
|
|
|
568 |
"" # Clear the input
|
569 |
)
|
570 |
|
571 |
+
# Toggle prompt visibility
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
572 |
def toggle_prompt_visibility():
|
|
|
573 |
return gr.update(visible=True)
|
574 |
|
575 |
+
# Save prompt
|
576 |
def save_prompt(team_num, prompt_text, app, prompts_dict):
|
577 |
+
"""Save a custom prompt for a team."""
|
578 |
if app is None:
|
579 |
+
app = FantasyDraftApp()
|
|
|
|
|
|
|
580 |
prompts_dict[team_num] = prompt_text
|
581 |
+
app.custom_prompts = prompts_dict
|
582 |
return app, prompts_dict, gr.update(visible=False)
|
583 |
|
584 |
+
# Run and check function
|
585 |
+
def run_and_check(app, prompts_dict):
|
586 |
+
"""Run the draft and check for user turns."""
|
|
|
587 |
if app is None:
|
588 |
+
app = FantasyDraftApp()
|
|
|
|
|
|
|
589 |
app.custom_prompts = prompts_dict
|
590 |
|
591 |
+
generator = app.run_multiagent_demo()
|
592 |
+
output = ""
|
593 |
+
|
594 |
+
for chunk in generator:
|
595 |
+
output = chunk
|
596 |
+
# Check if it's user's turn
|
597 |
+
if "<!--USER_TURN-->" in output:
|
598 |
+
break
|
599 |
+
yield output, app, gr.update(), gr.update(), gr.update(), ""
|
600 |
+
|
601 |
+
# Final yield with turn check
|
602 |
+
clean_output, controls_update, accordion_update, available_text, input_clear = check_user_turn(output, app)
|
603 |
+
yield clean_output, app, controls_update, accordion_update, available_text, input_clear
|
604 |
+
|
605 |
+
# Set up event handlers
|
606 |
+
# Team prompt toggles
|
607 |
+
team1_settings_btn.click(
|
608 |
+
toggle_prompt_visibility,
|
609 |
+
outputs=team1_prompt_col
|
610 |
+
)
|
611 |
+
team2_settings_btn.click(
|
612 |
+
toggle_prompt_visibility,
|
613 |
+
outputs=team2_prompt_col
|
614 |
+
)
|
615 |
+
team3_settings_btn.click(
|
616 |
+
toggle_prompt_visibility,
|
617 |
+
outputs=team3_prompt_col
|
618 |
+
)
|
619 |
+
team5_settings_btn.click(
|
620 |
+
toggle_prompt_visibility,
|
621 |
+
outputs=team5_prompt_col
|
622 |
+
)
|
623 |
+
team6_settings_btn.click(
|
624 |
+
toggle_prompt_visibility,
|
625 |
+
outputs=team6_prompt_col
|
626 |
+
)
|
627 |
|
628 |
+
# Save prompts
|
629 |
team1_save_btn.click(
|
630 |
+
lambda prompt, app, prompts: save_prompt(1, prompt, app, prompts),
|
631 |
+
inputs=[team1_prompt, app_state, agent_prompts],
|
632 |
+
outputs=[app_state, agent_prompts, team1_prompt_col]
|
633 |
)
|
634 |
team2_save_btn.click(
|
635 |
+
lambda prompt, app, prompts: save_prompt(2, prompt, app, prompts),
|
636 |
+
inputs=[team2_prompt, app_state, agent_prompts],
|
637 |
+
outputs=[app_state, agent_prompts, team2_prompt_col]
|
638 |
)
|
639 |
team3_save_btn.click(
|
640 |
+
lambda prompt, app, prompts: save_prompt(3, prompt, app, prompts),
|
641 |
+
inputs=[team3_prompt, app_state, agent_prompts],
|
642 |
+
outputs=[app_state, agent_prompts, team3_prompt_col]
|
643 |
)
|
644 |
team5_save_btn.click(
|
645 |
+
lambda prompt, app, prompts: save_prompt(5, prompt, app, prompts),
|
646 |
+
inputs=[team5_prompt, app_state, agent_prompts],
|
647 |
+
outputs=[app_state, agent_prompts, team5_prompt_col]
|
648 |
)
|
649 |
team6_save_btn.click(
|
650 |
+
lambda prompt, app, prompts: save_prompt(6, prompt, app, prompts),
|
651 |
+
inputs=[team6_prompt, app_state, agent_prompts],
|
652 |
+
outputs=[app_state, agent_prompts, team6_prompt_col]
|
653 |
)
|
654 |
|
655 |
+
# Start mock draft
|
656 |
run_multiagent_btn.click(
|
657 |
+
lambda app, prompts: run_and_check(app, prompts),
|
658 |
+
inputs=[app_state, agent_prompts],
|
659 |
+
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
660 |
)
|
661 |
|
662 |
+
# Submit pick button
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
663 |
submit_pick_btn.click(
|
664 |
submit_and_continue,
|
665 |
+
inputs=[draft_pick_input, app_state],
|
666 |
+
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input]
|
|
|
667 |
)
|
668 |
|
669 |
+
# Submit pick on Enter
|
670 |
draft_pick_input.submit(
|
671 |
submit_and_continue,
|
672 |
+
inputs=[draft_pick_input, app_state],
|
673 |
+
outputs=[multiagent_output, app_state, mock_draft_controls, available_accordion, available_players_display, draft_pick_input]
|
|
|
674 |
)
|
675 |
|
676 |
+
# Custom CSS for styling
|
677 |
demo.css = """
|
678 |
#main-container {
|
679 |
+
max-width: 1400px;
|
680 |
margin: 0 auto;
|
681 |
}
|
682 |
|
683 |
.multiagent-output {
|
684 |
+
max-height: 600px;
|
685 |
overflow-y: auto;
|
686 |
+
padding: 20px;
|
687 |
+
background: #f5f5f5;
|
688 |
+
border-radius: 8px;
|
689 |
}
|
690 |
|
691 |
+
#draft-pick-input {
|
692 |
+
font-size: 1.2em;
|
693 |
+
padding: 10px;
|
|
|
|
|
|
|
|
|
694 |
}
|
695 |
|
696 |
#start-button {
|
697 |
+
font-size: 1.2em;
|
698 |
+
padding: 15px 30px;
|
699 |
}
|
700 |
|
701 |
+
.monospace {
|
702 |
+
font-family: 'Courier New', monospace;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
703 |
}
|
704 |
"""
|
|
|
|
|
|
|
705 |
|
706 |
return demo
|
707 |
|
708 |
|
709 |
def main():
|
710 |
+
"""Launch the Gradio app."""
|
711 |
+
# Set environment variables for cloud deployment
|
712 |
+
if os.getenv("SPACE_ID"): # Running on Hugging Face Spaces
|
713 |
+
print("🤗 Running on Hugging Face Spaces")
|
714 |
+
os.environ["GRADIO_SERVER_NAME"] = "0.0.0.0"
|
715 |
+
os.environ["GRADIO_SERVER_PORT"] = "7860"
|
|
|
|
|
|
|
716 |
|
717 |
# Create and launch the interface
|
718 |
demo = create_gradio_interface()
|
719 |
|
720 |
+
# Launch with appropriate settings
|
721 |
+
demo.launch(
|
722 |
+
share=False,
|
723 |
+
server_name="0.0.0.0" if os.getenv("SPACE_ID") else None,
|
724 |
+
server_port=7860 if os.getenv("SPACE_ID") else None
|
725 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
726 |
|
727 |
|
728 |
if __name__ == "__main__":
|
apps/multiagent_draft.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
Multi-Agent Mock Draft Implementation
|
4 |
-
Demonstrates
|
5 |
"""
|
6 |
|
7 |
import time
|
|
|
1 |
#!/usr/bin/env python3
|
2 |
"""
|
3 |
Multi-Agent Mock Draft Implementation
|
4 |
+
Demonstrates agent communication and multi-turn memory
|
5 |
"""
|
6 |
|
7 |
import time
|
core/constants.py
CHANGED
@@ -1,13 +1,10 @@
|
|
1 |
"""
|
2 |
-
Constants for the Fantasy Draft
|
3 |
"""
|
4 |
|
5 |
# Timing constants (in seconds)
|
6 |
TYPING_DELAY_SECONDS = 0.5
|
7 |
MESSAGE_DELAY_SECONDS = 1.0
|
8 |
-
AGENT_START_DELAY = 5.0 # Increased from 3.0 to give agents more time to fully initialize
|
9 |
-
AGENT_STARTUP_WAIT = 1.0 # Increased from 0.5 to ensure each agent is ready
|
10 |
-
DEFAULT_TIMEOUT = 90.0 # Increased further for HF Spaces network latency
|
11 |
|
12 |
# Comment configuration
|
13 |
MAX_COMMENTS_PER_PICK = 1 # Reduced for more concise draft flow
|
@@ -19,48 +16,4 @@ RIVAL_PAIRS = {
|
|
19 |
5: [2, 6], # Upside Hunter vs BPA agents
|
20 |
2: 5, # BPA vs Upside Hunter
|
21 |
6: 5, # BPA vs Upside Hunter
|
22 |
-
}
|
23 |
-
|
24 |
-
# A2A Agent configurations
|
25 |
-
AGENT_CONFIGS = [
|
26 |
-
{
|
27 |
-
"team_name": "Team 1",
|
28 |
-
"team_num": 1,
|
29 |
-
"strategy": "Zero RB",
|
30 |
-
"port": 5001,
|
31 |
-
"philosophy": "RBs are INJURY MAGNETS! 🏥 WRs are the future! I'm building an AIR RAID OFFENSE that will DESTROY your pathetic ground game! 💨✈️",
|
32 |
-
"emoji_style": ["✈️", "💨", "🏥", "🎯"],
|
33 |
-
},
|
34 |
-
{
|
35 |
-
"team_name": "Team 2",
|
36 |
-
"team_num": 2,
|
37 |
-
"strategy": "BPA",
|
38 |
-
"port": 5002,
|
39 |
-
"philosophy": "I am the VALUE VULTURE! 🦅 I feast on your emotional reaches while I build a CHAMPIONSHIP ROSTER with pure analytics! 📊📈",
|
40 |
-
"emoji_style": ["🦅", "📊", "📈", "💰"],
|
41 |
-
},
|
42 |
-
{
|
43 |
-
"team_name": "Team 3",
|
44 |
-
"team_num": 3,
|
45 |
-
"strategy": "Robust RB",
|
46 |
-
"port": 5003,
|
47 |
-
"philosophy": "GROUND AND POUND, BABY! 💪 Your fancy WRs will be watching from the sidelines while my RBs BULLDOZE their way to victory! 🚜💥",
|
48 |
-
"emoji_style": ["💪", "🚜", "💥", "🏃"],
|
49 |
-
},
|
50 |
-
{
|
51 |
-
"team_name": "Team 5",
|
52 |
-
"team_num": 5,
|
53 |
-
"strategy": "Upside Hunter",
|
54 |
-
"port": 5005,
|
55 |
-
"philosophy": "BOOM OR BUST! 🎰🚀 Safe picks are for COWARDS! I'm swinging for the fences while you play it safe like a SCARED LITTLE MOUSE! 🐭💣",
|
56 |
-
"emoji_style": ["🎰", "🚀", "💣", "⚡"],
|
57 |
-
},
|
58 |
-
{
|
59 |
-
"team_name": "Team 6",
|
60 |
-
"team_num": 6,
|
61 |
-
"strategy": "BPA",
|
62 |
-
"port": 5006,
|
63 |
-
"philosophy": "Another spreadsheet warrior here to EXPLOIT your terrible decisions! 🤓💻 My algorithm laughs at your 'gut feelings'! 🤖📉",
|
64 |
-
"emoji_style": ["🤓", "💻", "🤖", "📉"],
|
65 |
-
},
|
66 |
-
]
|
|
|
1 |
"""
|
2 |
+
Constants for the Fantasy Draft Multi-Agent implementation.
|
3 |
"""
|
4 |
|
5 |
# Timing constants (in seconds)
|
6 |
TYPING_DELAY_SECONDS = 0.5
|
7 |
MESSAGE_DELAY_SECONDS = 1.0
|
|
|
|
|
|
|
8 |
|
9 |
# Comment configuration
|
10 |
MAX_COMMENTS_PER_PICK = 1 # Reduced for more concise draft flow
|
|
|
16 |
5: [2, 6], # Upside Hunter vs BPA agents
|
17 |
2: 5, # BPA vs Upside Hunter
|
18 |
6: 5, # BPA vs Upside Hunter
|
19 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/FEATURES_AND_ENHANCEMENTS.md
CHANGED
@@ -2,203 +2,143 @@
|
|
2 |
|
3 |
## Core Features
|
4 |
|
5 |
-
###
|
6 |
-
- **
|
7 |
-
- **
|
8 |
-
-
|
9 |
-
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
- **
|
15 |
-
- **
|
16 |
-
- **
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
- **
|
21 |
-
- **
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
- **
|
27 |
-
|
28 |
-
|
29 |
-
- **
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
- **
|
35 |
-
- **
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
- **
|
41 |
-
- **
|
42 |
-
- **
|
43 |
-
- **
|
44 |
-
|
45 |
-
|
46 |
-
- **
|
47 |
-
- **
|
48 |
-
- **
|
49 |
-
- **
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
- **Directed Messages**: Clear sender → recipient format
|
55 |
-
- **Commissioner Announcements**: Official draft updates
|
56 |
-
- **Trash Talk**: Agents comment on rivals' picks
|
57 |
-
- **Memory Indicators**: Shows when agents reference past events
|
58 |
-
|
59 |
-
#### Comment Limiting
|
60 |
-
- **Smart Throttling**: Max 2-3 comments per pick
|
61 |
-
- **Rival Priority**: Rivals more likely to comment
|
62 |
-
- **Natural Flow**: Prevents conversation overload
|
63 |
-
|
64 |
-
### 🔧 Technical Features
|
65 |
-
|
66 |
-
#### Dual Mode Operation
|
67 |
-
1. **Basic Multiagent Mode**
|
68 |
-
- Single process execution
|
69 |
-
- Fast response times
|
70 |
-
- Perfect for development
|
71 |
-
|
72 |
-
2. **A2A Mode (Agent-to-Agent)**
|
73 |
-
- Distributed architecture
|
74 |
-
- Each agent on HTTP server
|
75 |
-
- Production-ready setup
|
76 |
-
|
77 |
-
#### Multi-User Support
|
78 |
-
- **Session Isolation**: Each user gets separate instance
|
79 |
-
- **Gradio State Management**: Proper state handling
|
80 |
-
- **Dynamic Port Allocation**: No conflicts in A2A mode
|
81 |
-
- **Concurrent Users**: Supports multiple simultaneous drafts
|
82 |
-
|
83 |
-
### 📊 Draft Features
|
84 |
-
|
85 |
-
#### Snake Draft Logic
|
86 |
-
- **Round 1**: Picks 1→2→3→4→5→6
|
87 |
-
- **Round 2**: Picks 6→5→4→3→2→1 (reverses)
|
88 |
-
- **Round 3**: Picks 1→2→3→4→5→6
|
89 |
-
|
90 |
-
#### Player Database
|
91 |
-
- **50+ Players**: Real NFL players with positions
|
92 |
-
- **Team Info**: Current NFL team assignments
|
93 |
-
- **Positional Balance**: QBs, RBs, WRs, TEs
|
94 |
-
- **Realistic Rankings**: Based on fantasy relevance
|
95 |
-
|
96 |
-
#### Draft Intelligence
|
97 |
-
- **Strategy Adherence**: Agents follow their strategies
|
98 |
-
- **Contextual Decisions**: React to draft flow
|
99 |
-
- **Position Scarcity**: Recognize run on positions
|
100 |
-
- **Value Recognition**: Identify steals and reaches
|
101 |
-
|
102 |
-
### 🚀 Performance Optimizations
|
103 |
-
|
104 |
-
#### Configurable Delays
|
105 |
```python
|
106 |
-
TYPING_DELAY_SECONDS = 0.
|
107 |
-
MESSAGE_DELAY_SECONDS = 0
|
108 |
-
|
109 |
```
|
110 |
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
-
|
124 |
-
|
125 |
-
|
126 |
-
-
|
127 |
-
|
128 |
-
|
129 |
-
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
-
|
136 |
-
-
|
137 |
-
-
|
138 |
-
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
-
|
150 |
-
-
|
151 |
-
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
-
|
157 |
-
-
|
158 |
-
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
-
|
163 |
-
-
|
164 |
-
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
-
|
176 |
-
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
-
|
181 |
-
-
|
182 |
-
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
- Customize UI in `apps/app.py`
|
197 |
-
|
198 |
-
## Future Possibilities
|
199 |
-
- WebSocket real-time updates
|
200 |
-
- Custom league settings
|
201 |
-
- More agent personalities
|
202 |
-
- Advanced statistics
|
203 |
-
- Trade negotiations
|
204 |
-
- Dynasty league support
|
|
|
2 |
|
3 |
## Core Features
|
4 |
|
5 |
+
### 1. Interactive Mock Draft Experience
|
6 |
+
- **User Participation**: Play as Team 4 with 5 AI opponents
|
7 |
+
- **Real-time Advisor**: Get strategic recommendations based on:
|
8 |
+
- Your current roster needs
|
9 |
+
- Available player values
|
10 |
+
- Other teams' strategies
|
11 |
+
- Position scarcity
|
12 |
+
|
13 |
+
### 2. Advanced Multi-Agent System
|
14 |
+
- **6 Distinct Agents**: Each with unique strategies and personalities
|
15 |
+
- **Multi-Turn Memory**: Agents remember all interactions
|
16 |
+
- **Dynamic Comments**: Context-aware reactions to picks
|
17 |
+
- **Natural Rivalries**: Zero RB vs Robust RB create engaging banter
|
18 |
+
|
19 |
+
### 3. Visual Draft Experience
|
20 |
+
- **Live Draft Board**: See all picks organized by team
|
21 |
+
- **Round Progress**: Track current round and pick number
|
22 |
+
- **Team Rosters**: Visual representation of each team's selections
|
23 |
+
- **Available Players**: Searchable list with positions and teams
|
24 |
+
|
25 |
+
### 4. Customizable Agents
|
26 |
+
- **Personality Editor**: Modify each agent's trash-talk style
|
27 |
+
- **Strategy Tuning**: Adjust how aggressive or conservative agents are
|
28 |
+
- **Emoji Styles**: Each agent has their own emoji personality
|
29 |
+
- **Memory References**: Agents callback to earlier interactions
|
30 |
+
|
31 |
+
## Technical Enhancements
|
32 |
+
|
33 |
+
### 1. Memory System
|
34 |
+
- **Conversation History**: Full context maintained across turns
|
35 |
+
- **Pick Tracking**: Agents know who picked whom and when
|
36 |
+
- **Strategy Awareness**: Agents understand opponents' approaches
|
37 |
+
- **Grudge Memory**: Agents remember who mocked their picks
|
38 |
+
|
39 |
+
### 2. Performance Optimizations
|
40 |
+
- **Typing Effect**: Natural conversation flow with "..." indicators
|
41 |
+
- **Async Processing**: Non-blocking UI during agent turns
|
42 |
+
- **Efficient State Management**: Minimal re-renders
|
43 |
+
- **Smart Comment Selection**: Rivals prioritized for reactions
|
44 |
+
|
45 |
+
### 3. User Experience
|
46 |
+
- **Keyboard Shortcuts**: Enter to submit picks
|
47 |
+
- **Auto-scroll**: Keeps current action in view
|
48 |
+
- **Responsive Design**: Works on various screen sizes
|
49 |
+
- **Error Recovery**: Graceful handling of invalid picks
|
50 |
+
|
51 |
+
## Configuration Options
|
52 |
+
|
53 |
+
### Timing Controls
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
```python
|
55 |
+
TYPING_DELAY_SECONDS = 0.5 # "..." display time
|
56 |
+
MESSAGE_DELAY_SECONDS = 1.0 # Between messages
|
57 |
+
MAX_COMMENTS_PER_PICK = 1 # Keeps draft moving
|
58 |
```
|
59 |
|
60 |
+
### Rivalry System
|
61 |
+
```python
|
62 |
+
RIVAL_PAIRS = {
|
63 |
+
1: 3, # Zero RB vs Robust RB
|
64 |
+
3: 1, # Mutual rivalry
|
65 |
+
5: [2, 6], # Upside vs BPA agents
|
66 |
+
}
|
67 |
+
```
|
68 |
+
|
69 |
+
## Agent Strategies
|
70 |
+
|
71 |
+
### 1. Zero RB (Team 1)
|
72 |
+
- Avoids RBs in early rounds
|
73 |
+
- Prioritizes elite WRs
|
74 |
+
- Mocks traditional RB-heavy approaches
|
75 |
+
- Gets RB value in later rounds
|
76 |
+
|
77 |
+
### 2. Best Player Available (Teams 2 & 6)
|
78 |
+
- Pure value-based drafting
|
79 |
+
- Ignores positional needs
|
80 |
+
- Critical of "reaching" for positions
|
81 |
+
- Cold, analytical approach
|
82 |
+
|
83 |
+
### 3. Robust RB (Team 3)
|
84 |
+
- RBs in rounds 1-2 mandatory
|
85 |
+
- Old-school philosophy
|
86 |
+
- Hates modern passing game trends
|
87 |
+
- Values "workhorse" backs
|
88 |
+
|
89 |
+
### 4. Upside Hunter (Team 5)
|
90 |
+
- Seeks high-ceiling players
|
91 |
+
- Willing to take risks
|
92 |
+
- Mocks "safe" picks
|
93 |
+
- Boom-or-bust mentality
|
94 |
+
|
95 |
+
## Unique Implementation Details
|
96 |
+
|
97 |
+
### 1. Dynamic Prompt Generation
|
98 |
+
- Context-aware responses based on:
|
99 |
+
- Current round
|
100 |
+
- Previous picks
|
101 |
+
- Available players
|
102 |
+
- Recent conversations
|
103 |
+
|
104 |
+
### 2. Natural Language Processing
|
105 |
+
- Agents avoid raw statistics in speech
|
106 |
+
- Convert ADP numbers to natural phrases
|
107 |
+
- Use strategy-appropriate vocabulary
|
108 |
+
- Maintain consistent personality
|
109 |
+
|
110 |
+
### 3. Competitive Dynamics
|
111 |
+
- Agents defend their strategies aggressively
|
112 |
+
- No polite, generic responses
|
113 |
+
- Real draft room atmosphere
|
114 |
+
- Escalating rivalries as draft progresses
|
115 |
+
|
116 |
+
## Future Enhancement Ideas
|
117 |
+
|
118 |
+
1. **Draft Analysis**
|
119 |
+
- Post-draft grades for each team
|
120 |
+
- Strategy effectiveness metrics
|
121 |
+
- Win probability projections
|
122 |
+
|
123 |
+
2. **Extended Rounds**
|
124 |
+
- Support for full 15+ round drafts
|
125 |
+
- Bench strategy considerations
|
126 |
+
- Late-round sleeper picks
|
127 |
+
|
128 |
+
3. **Custom Scoring**
|
129 |
+
- PPR vs Standard scoring impacts
|
130 |
+
- Dynasty league considerations
|
131 |
+
- Keeper league strategies
|
132 |
+
|
133 |
+
4. **Historical Data**
|
134 |
+
- Previous season performance
|
135 |
+
- Injury history considerations
|
136 |
+
- Breakout candidate identification
|
137 |
+
|
138 |
+
## Tips for Best Experience
|
139 |
+
|
140 |
+
1. **Starting Fresh**: Each draft is independent
|
141 |
+
2. **Pick Timing**: Take your time, agents will wait
|
142 |
+
3. **Name Matching**: Type player names as shown
|
143 |
+
4. **Enjoy the Banter**: Let agents build rivalries
|
144 |
+
5. **Experiment**: Try different strategies yourself
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
@@ -5,31 +5,18 @@ python-dotenv
|
|
5 |
pydantic>=2.0.0
|
6 |
typing-extensions
|
7 |
nest-asyncio
|
8 |
-
aiohttp
|
9 |
|
10 |
-
# Step 2: Install
|
11 |
-
# Pin to specific version for consistency
|
12 |
-
a2a-sdk==0.2.9
|
13 |
-
|
14 |
-
# Step 3: Install any-agent with both a2a and openai extras
|
15 |
# Version 0.22+ required for any_agent.serving module
|
16 |
-
any-agent[
|
17 |
|
18 |
-
# Step
|
19 |
# These might already be installed by any-agent[openai] but we list them to be sure
|
20 |
openai>=1.0.0
|
21 |
litellm>=1.0.0
|
22 |
|
23 |
-
# Step
|
24 |
gradio==4.36.0
|
25 |
|
26 |
# Compatibility pins to satisfy transitive dependency constraints discovered during HF Spaces build
|
27 |
markupsafe==2.1.5 # gradio constraint (~=2.0)
|
28 |
-
fsspec>=2023.1.0,<=2025.3.0 # datasets constraint (<=2025.3.0)
|
29 |
-
|
30 |
-
# Optional: HTTP dependencies for lightweight A2A
|
31 |
-
httpx>=0.24.0
|
32 |
-
fastapi>=0.115.0
|
33 |
-
uvicorn>=0.22.0
|
34 |
-
starlette>=0.46.0,<0.47.0
|
35 |
-
sse-starlette>=2.3.6,<2.4.0
|
|
|
5 |
pydantic>=2.0.0
|
6 |
typing-extensions
|
7 |
nest-asyncio
|
|
|
8 |
|
9 |
+
# Step 2: Install any-agent with OpenAI support
|
|
|
|
|
|
|
|
|
10 |
# Version 0.22+ required for any_agent.serving module
|
11 |
+
any-agent[openai]>=0.22.0
|
12 |
|
13 |
+
# Step 3: Additional dependencies (if not pulled in by any-agent)
|
14 |
# These might already be installed by any-agent[openai] but we list them to be sure
|
15 |
openai>=1.0.0
|
16 |
litellm>=1.0.0
|
17 |
|
18 |
+
# Step 4: Gradio last (it has many dependencies)
|
19 |
gradio==4.36.0
|
20 |
|
21 |
# Compatibility pins to satisfy transitive dependency constraints discovered during HF Spaces build
|
22 |
markupsafe==2.1.5 # gradio constraint (~=2.0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|