alexmec commited on
Commit
a78aa96
·
verified ·
1 Parent(s): db8016a

Upload folder using huggingface_hub

Browse files
app.py CHANGED
@@ -1,64 +1,19 @@
1
  #!/usr/bin/env python3
2
  """
3
- Entry point for Hugging Face Spaces
 
4
  """
5
 
6
- import sys
7
  import os
8
- import subprocess
9
 
10
- # Check if we're on Hugging Face Spaces
11
  if os.getenv("SPACE_ID"):
12
  print("🤗 Running on Hugging Face Spaces...")
13
- print("ℹ️ Checking A2A dependencies...")
14
-
15
- # Try to import a2a, install if needed
16
- try:
17
- import a2a
18
- print("✅ a2a module already available")
19
- except ImportError:
20
- print("⚠️ a2a module not found, attempting to install...")
21
- try:
22
- subprocess.check_call([sys.executable, "-m", "pip", "install", "a2a-sdk==0.2.9"])
23
- import a2a
24
- print("✅ Successfully installed and imported a2a!")
25
- except Exception as e:
26
- print(f"❌ Failed to install a2a-sdk: {e}")
27
-
28
- # Now test the full A2A imports
29
- try:
30
- from any_agent.serving import A2AServingConfig
31
- from any_agent.tools import a2a_tool_async
32
- print("✅ Full A2A dependencies are available! A2A mode will work.")
33
- except ImportError as e:
34
- print(f"⚠️ A2A components not available from any_agent: {e}")
35
- # Try to fix by reinstalling any-agent with a2a extra
36
- print("📦 Attempting to install any-agent[a2a]...")
37
- try:
38
- # Install or upgrade any-agent with the required extras. We intentionally
39
- # avoid pinning to an older version here because versions prior to
40
- # 0.22.0 do not expose `any_agent.serving`.
41
- subprocess.check_call([
42
- sys.executable,
43
- "-m",
44
- "pip",
45
- "install",
46
- "any-agent[a2a,openai]>=0.22.0",
47
- ])
48
- # Try import again
49
- from any_agent.serving import A2AServingConfig
50
- from any_agent.tools import a2a_tool_async
51
- print("✅ Successfully installed! A2A mode will work.")
52
- except Exception as e2:
53
- print(f"❌ Failed to install any-agent[a2a]: {e2}")
54
- print("✅ Basic Multiagent mode will be used instead.")
55
-
56
  else:
57
- # Not on HF Spaces
58
  print("🖥️ Running locally...")
59
 
60
- # Import and run the enhanced app
61
- from apps.app_enhanced import main
62
 
63
  if __name__ == "__main__":
64
  main()
 
1
  #!/usr/bin/env python3
2
  """
3
+ Entry point for Hugging Face Spaces and local deployment.
4
+ All dependencies should be installed via requirements.txt.
5
  """
6
 
 
7
  import os
 
8
 
9
+ # Simple startup message
10
  if os.getenv("SPACE_ID"):
11
  print("🤗 Running on Hugging Face Spaces...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  else:
 
13
  print("🖥️ Running locally...")
14
 
15
+ # Import and run the main app
16
+ from apps.app import main
17
 
18
  if __name__ == "__main__":
19
  main()
apps/README.md ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Apps Directory
2
+
3
+ This directory contains the main application and supporting modules:
4
+
5
+ ## Main Files
6
+
7
+ - **`app.py`** - The main Gradio web interface with full A2A support
8
+ - **`multiagent_draft.py`** - Core draft logic and agent management
9
+ - **`multiagent_scenarios.py`** - UI formatting, visualization, and agent communication
10
+
11
+ ## Notes
12
+
13
+ - The main entry point for the application is `/fantasy-draft-agent/app.py` which imports from this directory
14
+ - Previously there were two versions (app.py and app_enhanced.py) but they have been consolidated
15
+ - The `.bak` file is a backup of the older version without A2A support
apps/app.py CHANGED
@@ -1,13 +1,15 @@
1
  #!/usr/bin/env python3
2
  """
3
- Fantasy Draft Multi-Agent Demo
4
- Showcases multi-agent and multi-turn capabilities
5
  """
6
 
7
  import os
8
  import time
9
  import gradio as gr
10
- from typing import List, Tuple
 
 
11
  from dotenv import load_dotenv
12
  import sys
13
  import os
@@ -15,6 +17,25 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
15
 
16
  from core.agent import FantasyDraftAgent
17
  from core.data import TOP_PLAYERS
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  from apps.multiagent_draft import MultiAgentMockDraft
19
  from apps.multiagent_scenarios import (
20
  run_interactive_mock_draft,
@@ -24,40 +45,319 @@ from apps.multiagent_scenarios import (
24
  create_mock_draft_visualization
25
  )
26
 
 
 
 
 
 
 
27
  # Fix for litellm 1.72.4 OpenAI endpoint issue
28
- # This ensures litellm uses the correct OpenAI API endpoint
29
  os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1'
30
 
31
- # Load environment variables from .env file
32
  load_dotenv()
33
 
34
 
35
- class FantasyDraftApp:
36
  def __init__(self):
37
  self.current_draft = None # Store the current mock draft
38
  self.draft_output = "" # Store the draft output so far
 
 
 
 
39
 
40
- def run_multiagent_demo(self):
41
- """Run the mock draft demonstration."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  # Reset any previous draft
43
  self.current_draft = None
44
  self.draft_output = ""
45
 
46
- # Run the draft generator
47
- draft_generator = run_interactive_mock_draft()
 
48
 
49
- for output in draft_generator:
50
- # Check if this is a tuple (draft state, output)
51
- if isinstance(output, tuple):
52
- # This means it's the user's turn
53
- self.current_draft, self.draft_output = output
54
- # Add a special marker for Gradio to detect
55
- yield self.draft_output + "\n<!--USER_TURN-->"
56
- return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  else:
58
- # Regular output
59
- self.draft_output = output
60
- yield output
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
  def continue_mock_draft(self, player_name: str):
63
  """Continue the mock draft after user makes a pick."""
@@ -79,46 +379,52 @@ class FantasyDraftApp:
79
 
80
  # Check if it's a typing indicator - skip it
81
  if isinstance(agent, str) and agent.startswith("typing_"):
82
- continue # Skip typing indicators, we'll handle inline
83
  else:
84
  # Show "..." first for typing effect
85
  typing_placeholder = format_agent_message(agent, recipient, "...")
86
  self.draft_output += typing_placeholder
87
  yield self.draft_output
88
- time.sleep(0.5) # Brief typing delay
89
 
90
  # Replace "..." with actual message
91
  self.draft_output = self.draft_output.replace(typing_placeholder, "")
92
  self.draft_output += format_agent_message(agent, recipient, content)
93
  yield self.draft_output
94
- time.sleep(1.0) # Reading delay
95
 
96
- # Continue the draft from where we left off
97
- # We need to track where we were in the draft
 
 
 
 
 
 
 
98
  total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
99
- current_round = ((total_picks - 1) // 6) + 1 # 6 teams per round
100
 
101
- # Continue with the rest of the draft
102
- draft_memories = []
103
 
104
- # Continue the draft
105
- for round_num in range(current_round, 4): # Continue from current round to round 3
106
  if round_num > current_round:
107
  self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
108
  yield self.draft_output
109
 
110
- # Snake draft order - 6 teams total
111
  if round_num % 2 == 1:
112
- pick_order = list(range(1, 7)) # 1-6 for odd rounds
113
  else:
114
- pick_order = list(range(6, 0, -1)) # 6-1 for even rounds
115
 
116
  # Calculate where we are in the current round
117
- picks_in_round = total_picks % 6 # 6 teams per round
118
  start_idx = picks_in_round if round_num == current_round else 0
119
 
120
  for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1):
121
- pick_num = (round_num - 1) * 6 + pick_in_round # 6 teams per round
122
 
123
  # Show draft board at start of round
124
  if pick_in_round == 1:
@@ -126,37 +432,108 @@ class FantasyDraftApp:
126
  self.draft_output += "\n"
127
  yield self.draft_output
128
 
129
- # Process the pick
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  messages, result = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
131
 
132
- # Display messages with inline typing effect
133
  for msg in messages:
134
  if len(msg) >= 3:
135
  agent, recipient, content = msg[:3]
136
 
137
- # Check if it's a typing indicator - skip it
138
  if isinstance(agent, str) and agent.startswith("typing_"):
139
- continue # Skip typing indicators, we'll handle inline
140
  else:
141
- # Show "..." first for typing effect
142
  typing_placeholder = format_agent_message(agent, recipient, "...")
143
  self.draft_output += typing_placeholder
144
  yield self.draft_output
145
- time.sleep(0.5) # Brief typing delay
146
 
147
- # Replace "..." with actual message
148
  self.draft_output = self.draft_output.replace(typing_placeholder, "")
149
  self.draft_output += format_agent_message(agent, recipient, content)
150
  yield self.draft_output
151
- time.sleep(1.0) # Reading delay
152
 
153
  if result is None:
154
- # It's the user's turn again
155
  self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
156
  yield self.draft_output + "\n<!--USER_TURN-->"
157
  return
158
 
159
- # Add memory indicators
160
  if round_num > 1 and pick_in_round % 2 == 0:
161
  if team_num in self.current_draft.agents:
162
  agent = self.current_draft.agents[team_num]
@@ -168,38 +545,61 @@ class FantasyDraftApp:
168
  self.draft_output += format_memory_indicator(round_num, draft_memories[-2:])
169
  yield self.draft_output
170
 
171
- time.sleep(0.5)
172
 
173
- # End of round
174
  self.draft_output += format_agent_message("commissioner", "ALL",
175
  f"That's the end of Round {round_num}!")
176
  yield self.draft_output
177
 
178
- # Final summary
179
  self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
180
  self.draft_output += self.current_draft.get_draft_summary()
181
  yield self.draft_output
182
 
183
- # Clear the draft state
184
  self.current_draft = None
185
 
186
 
187
  def create_gradio_interface():
188
- """Create the main Gradio interface."""
189
- app = FantasyDraftApp()
190
 
191
- with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Glass()) as demo:
 
 
 
192
  with gr.Column(elem_id="main-container"):
193
  gr.Markdown("""
194
  # 🏈 Fantasy Draft Multi-Agent Demo
195
 
196
- **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.
197
  """)
198
 
199
  with gr.Tabs():
200
  # Demo Tab
201
  with gr.TabItem("🎮 Demo"):
202
- # Show agent cards first
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  gr.Markdown("""
204
  ### 🏈 Meet Your Competition
205
 
@@ -212,14 +612,14 @@ def create_gradio_interface():
212
  gr.Markdown("""
213
  <div style="background-color: #E3F2FD; border-left: 4px solid #1976D2; padding: 15px; border-radius: 8px;">
214
 
215
- <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">📘🤓 Team 1 - Zero RB</h4>
216
 
217
- <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>
218
 
219
- <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
220
- <li style="color: #1a237e !important;">Avoids RBs early</li>
221
- <li style="color: #1a237e !important;">Loads up on WRs</li>
222
- <li style="color: #1a237e !important;">Gets RB value late</li>
223
  </ul>
224
  </div>
225
  """)
@@ -228,14 +628,14 @@ def create_gradio_interface():
228
  gr.Markdown("""
229
  <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
230
 
231
- <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗🧑‍💼 Team 2 - BPA</h4>
232
 
233
- <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>
234
 
235
- <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
236
- <li style="color: #1b5e20 !important;">Pure value drafting</li>
237
- <li style="color: #1b5e20 !important;">Ignores needs</li>
238
- <li style="color: #1b5e20 !important;">Mocks reaching</li>
239
  </ul>
240
  </div>
241
  """)
@@ -244,14 +644,14 @@ def create_gradio_interface():
244
  gr.Markdown("""
245
  <div style="background-color: #FFF3E0; border-left: 4px solid #F57C00; padding: 15px; border-radius: 8px;">
246
 
247
- <h4 style="color: #e65100 !important; margin: 0 0 10px 0;">📙🧔 Team 3 - Robust RB</h4>
248
 
249
- <p style="color: #ef6c00 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs win championships. Period."</p>
250
 
251
- <ul style="color: #e65100 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
252
- <li style="color: #e65100 !important;">RBs in rounds 1-2</li>
253
- <li style="color: #e65100 !important;">Old-school approach</li>
254
- <li style="color: #e65100 !important;">Foundation first</li>
255
  </ul>
256
  </div>
257
  """)
@@ -260,14 +660,14 @@ def create_gradio_interface():
260
  gr.Markdown("""
261
  <div style="background-color: #E8EAF6; border-left: 4px solid #3F51B5; padding: 15px; border-radius: 8px;">
262
 
263
- <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">👤 Position 4 - YOU</h4>
264
 
265
- <p style="color: #3949ab !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">Your draft position with AI guidance</p>
266
 
267
- <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
268
- <li style="color: #1a237e !important;">📕🧙 Strategic advisor</li>
269
- <li style="color: #1a237e !important;">Real-time guidance</li>
270
- <li style="color: #1a237e !important;">Roster analysis</li>
271
  </ul>
272
  </div>
273
  """)
@@ -276,14 +676,14 @@ def create_gradio_interface():
276
  gr.Markdown("""
277
  <div style="background-color: #F5E6FF; border-left: 4px solid #7B1FA2; padding: 15px; border-radius: 8px;">
278
 
279
- <h4 style="color: #4a148c !important; margin: 0 0 10px 0;">📓🤠 Team 5 - Upside</h4>
280
 
281
- <p style="color: #6a1b9a !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Safe picks are for losers!"</p>
282
 
283
- <ul style="color: #4a148c !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
284
- <li style="color: #4a148c !important;">Seeks breakouts</li>
285
- <li style="color: #4a148c !important;">High risk/reward</li>
286
- <li style="color: #4a148c !important;">Mocks safety</li>
287
  </ul>
288
  </div>
289
  """)
@@ -292,24 +692,24 @@ def create_gradio_interface():
292
  gr.Markdown("""
293
  <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
294
 
295
- <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗👨‍🏫 Team 6 - BPA</h4>
296
 
297
- <p style="color: #2e7d32 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Another value drafter to punish reaches."</p>
298
 
299
- <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
300
- <li style="color: #1b5e20 !important;">Takes obvious value</li>
301
- <li style="color: #1b5e20 !important;">Disciplined approach</li>
302
- <li style="color: #1b5e20 !important;">No sentiment</li>
303
  </ul>
304
  </div>
305
  """)
306
 
307
  gr.Markdown("""
308
  ### 🎮 Draft Format
309
- - **3 Rounds** of snake draft (1→6, 6→1, 1→6)
310
- - **Real-time trash talk** between picks
311
- - **Strategic advisor** guides your selections
312
- - **Memory system** - agents remember and reference earlier picks
313
 
314
  Ready to experience the most realistic AI draft room?
315
  """)
@@ -364,11 +764,20 @@ def create_gradio_interface():
364
 
365
  ### 💬 Agent-to-Agent (A2A) Communication
366
 
367
- Agents can:
368
- - **Comment on picks**: React to other agents' selections
369
- - **Respond to comments**: Defend their strategies
370
- - **Remember debates**: Reference earlier conversations
371
- - **Adapt strategies**: Adjust based on draft flow
 
 
 
 
 
 
 
 
 
372
 
373
  ### 📊 Architecture Flow
374
  """)
@@ -417,6 +826,7 @@ def create_gradio_interface():
417
  3. **Dynamic Adaptation**: Agents adjust based on draft progression
418
  4. **Natural Dialogue**: Human-like commentary and debates
419
  5. **User Integration**: Seamless human participation with AI guidance
 
420
 
421
  ### 📝 Implementation Details
422
 
@@ -424,22 +834,26 @@ def create_gradio_interface():
424
  - **Message Formatting**: Custom HTML/CSS for visual distinction
425
  - **State Management**: Draft board tracking and validation
426
  - **Memory Indicators**: Visual cues showing context retention
 
427
 
428
  ### 🚀 Why This Matters
429
 
430
  This demo proves that sophisticated multi-agent systems can be built with minimal code,
431
  showcasing the power of modern LLMs when properly orchestrated. The any-agent framework
432
  makes it easy to create agents that truly communicate and remember, not just respond.
 
 
 
433
  """)
434
 
435
  # Function to check if it's user's turn and show/hide controls
436
- def check_user_turn(output_text):
437
  """Check if output indicates it's user's turn."""
438
  if "<!--USER_TURN-->" in output_text:
439
  # Remove the marker from display
440
  clean_output = output_text.replace("<!--USER_TURN-->", "")
441
  # Get available players
442
- if app.current_draft:
443
  available = app.current_draft.get_available_players()
444
  available_text = "Available Players:\n\n"
445
  for player in sorted(available)[:20]: # Show top 20
@@ -465,235 +879,244 @@ def create_gradio_interface():
465
  "" # Clear the input
466
  )
467
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
468
  # Run multi-agent demo with control visibility handling
469
- def run_and_check():
470
  """Run demo and check for user turn."""
471
- for output in app.run_multiagent_demo():
472
- result = check_user_turn(output)
473
- yield result
 
 
 
 
 
474
 
475
  run_multiagent_btn.click(
476
  run_and_check,
477
- None,
478
- [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
479
  show_progress=True
480
  )
481
 
 
 
 
 
 
 
 
482
  # Continue draft after user pick
483
- def submit_and_continue(player_name):
484
  """Submit pick and continue draft."""
 
 
 
 
 
485
  for output in app.continue_mock_draft(player_name):
486
- result = check_user_turn(output)
487
- yield result
488
 
489
  submit_pick_btn.click(
490
  submit_and_continue,
491
- draft_pick_input,
492
- [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
493
  show_progress=True
494
  )
495
 
496
  # Also submit on enter
497
  draft_pick_input.submit(
498
  submit_and_continue,
499
- draft_pick_input,
500
- [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
501
  show_progress=True
502
  )
503
 
504
- # Add custom CSS for better styling
505
  demo.css = """
506
- /* Force white text on dark background for all main content */
507
- .gradio-container {
508
- max-width: 800px !important;
509
- margin: 0 auto !important;
510
- color: white !important;
511
- }
512
-
513
- /* Ensure all text elements are white by default */
514
- .gradio-container p,
515
- .gradio-container h1,
516
- .gradio-container h2,
517
- .gradio-container h3,
518
- .gradio-container h4,
519
- .gradio-container h5,
520
- .gradio-container h6,
521
- .gradio-container span,
522
- .gradio-container div,
523
- .gradio-container label,
524
- .gradio-container .markdown,
525
- .gradio-container .prose {
526
- color: white !important;
527
- }
528
-
529
- /* Ensure markdown content is white */
530
- .markdown-text,
531
- .markdown-text p,
532
- .markdown-text li,
533
- .markdown-text ul,
534
- .markdown-text ol {
535
- color: white !important;
536
- }
537
-
538
  #main-container {
539
- text-align: center;
540
- color: white !important;
541
- }
542
-
543
- /* Left-align text in How It Works tab */
544
- .tabitem:nth-child(2) {
545
- text-align: left !important;
546
  }
547
 
548
- #start-button {
549
- margin: 20px auto !important;
550
- max-width: 300px !important;
551
  }
552
 
553
- /* Only force dark text inside colored message boxes */
554
- div[style*="background-color"][style*="border-left"] {
555
  color: #212121 !important;
556
  }
557
 
558
- div[style*="background-color"][style*="border-left"] p,
559
- div[style*="background-color"][style*="border-left"] strong,
560
- div[style*="background-color"][style*="border-left"] em,
561
- div[style*="background-color"][style*="border-left"] span,
562
- div[style*="background-color"][style*="border-left"] li,
563
- div[style*="background-color"][style*="border-left"] ul,
564
- div[style*="background-color"][style*="border-left"] h1,
565
- div[style*="background-color"][style*="border-left"] h2,
566
- div[style*="background-color"][style*="border-left"] h3,
567
- div[style*="background-color"][style*="border-left"] h4 {
568
  color: #212121 !important;
569
  }
570
 
571
- /* System messages with yellow background */
572
- div[style*="#FFF9C4"] {
573
- color: #F57C00 !important;
574
- }
575
-
576
- /* Memory boxes */
577
- div[style*="#F5F5F5"] {
578
- color: #424242 !important;
579
- }
580
-
581
- #draft-pick-input {
582
- font-size: 1.1em;
583
- }
584
-
585
- /* Ensure tab labels are visible */
586
- .tab-nav button {
587
- color: white !important;
588
- }
589
-
590
- /* Ensure multiagent output text is white */
591
- .multiagent-output {
592
- color: white !important;
593
- }
594
-
595
- .multiagent-output p,
596
- .multiagent-output h1,
597
- .multiagent-output h2,
598
- .multiagent-output h3,
599
- .multiagent-output h4,
600
- .multiagent-output h5,
601
- .multiagent-output h6,
602
- .multiagent-output span,
603
- .multiagent-output div {
604
- color: white !important;
605
- }
606
-
607
- /* Specific rules for dark mode - target Gradio's dark theme class */
608
- .dark .gradio-container,
609
- .dark .gradio-container *:not([style*="background-color"]) {
610
- color: white !important;
611
- }
612
-
613
- /* Ensure description text under title is white */
614
- .gradio-container > div > div > div > p {
615
- color: white !important;
616
- }
617
-
618
- /* Tab content text */
619
- .tabitem .markdown-text {
620
- color: white !important;
621
- }
622
-
623
- /* Input labels and text */
624
- .gradio-container label {
625
- color: rgba(255, 255, 255, 0.9) !important;
626
- }
627
-
628
- /* Button text that's not in primary buttons */
629
- button:not(.primary) {
630
- color: rgba(255, 255, 255, 0.9) !important;
631
- }
632
-
633
- /* Fix for bold player names - ensure they're visible */
634
- .multiagent-output strong,
635
- .multiagent-output b {
636
- color: inherit !important;
637
- font-weight: 700;
638
- }
639
-
640
- /* Ensure bold text in message backgrounds has proper color */
641
- div[style*="background-color"] strong,
642
- div[style*="background-color"] b {
643
- color: inherit !important;
644
- }
645
-
646
- /* Specific fix for bold text in different message backgrounds */
647
- div[style*="background-color: #E3F2FD"] strong, /* Team messages */
648
- div[style*="background-color: #FFF8E1"] strong, /* Commissioner */
649
- div[style*="background-color: #FFEBEE"] strong, /* Advisor */
650
- div[style*="background-color: #F3E5F5"] strong { /* Memory */
651
- color: #1a1a1a !important;
652
- }
653
-
654
- /* Inline typing effect */
655
- .typing-dots {
656
- animation: pulse 1.0s ease-in-out infinite;
657
- }
658
-
659
- @keyframes pulse {
660
- 0%, 100% { opacity: 0.6; }
661
- 50% { opacity: 1; }
662
  }
663
  """
 
 
 
664
 
665
  return demo
666
 
667
 
668
  def main():
669
- """Launch the Gradio app."""
670
- import sys
671
-
672
- # Check for --share flag
673
- share_mode = "--share" in sys.argv or "-s" in sys.argv
674
-
675
- # Check for API key
676
  if not os.getenv("OPENAI_API_KEY"):
677
- print("\n⚠️ Warning: OPENAI_API_KEY not found in environment variables.")
678
- print("The app will launch but agent responses will fail without an API key.")
679
- print("Set it using: export OPENAI_API_KEY='your-key-here'\n")
 
 
 
680
 
681
- print("🚀 Launching Fantasy Draft Multi-Agent Demo...")
 
682
 
683
- if share_mode:
684
- print("🌐 Creating public share link (expires in 72 hours)...")
685
- print("📡 Please wait for the public URL...\n")
686
- else:
687
- print("📡 The app will be available at http://localhost:7860")
688
- print("💡 Tip: Use 'python app.py --share' to create a public link\n")
689
 
690
- demo = create_gradio_interface()
691
- demo.launch(
692
- server_name="0.0.0.0",
693
- server_port=7860,
694
- share=share_mode, # Enable sharing if flag is present
695
- show_error=True
696
- )
 
 
 
697
 
698
 
699
  if __name__ == "__main__":
 
1
  #!/usr/bin/env python3
2
  """
3
+ Fantasy Draft Multi-Agent Demo - Enhanced with A2A Support
4
+ Combines the superior UI from the main app with real A2A capabilities
5
  """
6
 
7
  import os
8
  import time
9
  import gradio as gr
10
+ import asyncio
11
+ import nest_asyncio
12
+ from typing import List, Tuple, Optional, Dict
13
  from dotenv import load_dotenv
14
  import sys
15
  import os
 
17
 
18
  from core.agent import FantasyDraftAgent
19
  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 (
41
  run_interactive_mock_draft,
 
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
+
54
  # Fix for litellm 1.72.4 OpenAI endpoint issue
 
55
  os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1'
56
 
57
+ # Load environment variables
58
  load_dotenv()
59
 
60
 
61
+ class EnhancedFantasyDraftApp:
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
 
70
+ async def toggle_a2a_mode(self, use_a2a: bool):
71
+ """Toggle between basic multiagent and A2A modes."""
72
+ self.use_real_a2a = use_a2a
73
+
74
+ if use_a2a:
75
+ # Lazy import A2A components only when needed
76
+ try:
77
+ global DynamicA2AAgentManager, cleanup_session
78
+ from core.dynamic_a2a_manager import DynamicA2AAgentManager, cleanup_session
79
+ self.real_a2a = True
80
+ self.a2a_type = "full"
81
+ except ImportError as e:
82
+ # Fall back to simulated A2A
83
+ try:
84
+ from core.simulated_a2a_manager import SimulatedA2AAgentManager, cleanup_session
85
+ DynamicA2AAgentManager = SimulatedA2AAgentManager
86
+ self.real_a2a = False
87
+ self.a2a_type = "simulated"
88
+ print("Using simulated A2A mode (real A2A not available)")
89
+ except ImportError as e2:
90
+ self.a2a_status = f"❌ A2A mode not available: {str(e)}. Please use Basic Multiagent mode."
91
+ self.use_real_a2a = False
92
+ return self.a2a_status
93
+
94
+ # Generate unique session ID if needed
95
+ if not self.session_id:
96
+ import uuid
97
+ self.session_id = str(uuid.uuid4())[:8]
98
+
99
+ # Create new dynamic manager for this session
100
+ self.a2a_manager = DynamicA2AAgentManager(self.session_id)
101
+
102
+ try:
103
+ await self.a2a_manager.start_agents()
104
+ ports = self.a2a_manager.allocated_ports
105
+ if hasattr(self, 'a2a_type'):
106
+ if self.a2a_type == "full":
107
+ self.a2a_status = f"✅ Full A2A Mode Active (Session: {self.session_id}, Ports: {ports[0]}-{ports[-1]})"
108
+ elif self.a2a_type == "lightweight":
109
+ self.a2a_status = f"✅ Lightweight A2A Mode Active (Session: {self.session_id}, HTTP Ports: {ports[0]}-{ports[-1]})"
110
+ else: # simulated
111
+ self.a2a_status = f"✅ Simulated A2A Mode Active (Session: {self.session_id}, Mock Ports: {ports[0]}-{ports[-1]})"
112
+ else:
113
+ self.a2a_status = f"✅ A2A Mode Active (Session: {self.session_id}, Ports: {ports[0]}-{ports[-1]})"
114
+ except RuntimeError as e:
115
+ # Failed to allocate ports or start agents
116
+ self.a2a_status = f"❌ Failed to start A2A: {str(e)}"
117
+ self.use_real_a2a = False
118
+ self.a2a_manager = None
119
+ else:
120
+ if self.a2a_manager and cleanup_session:
121
+ await cleanup_session(self.a2a_manager)
122
+ self.a2a_manager = None
123
+ self.a2a_status = "✅ Basic Multiagent Mode Active (Using built-in communication)"
124
+
125
+ return self.a2a_status
126
+
127
+ def run_multiagent_demo(self, use_a2a: bool = False):
128
+ """Run the mock draft demonstration with optional A2A support."""
129
  # Reset any previous draft
130
  self.current_draft = None
131
  self.draft_output = ""
132
 
133
+ # First, set the mode (like in the working version)
134
+ loop = asyncio.new_event_loop()
135
+ asyncio.set_event_loop(loop)
136
 
137
+ status = loop.run_until_complete(self.toggle_a2a_mode(use_a2a))
138
+ yield f"**Mode:** {status}\n\n"
139
+
140
+ # Initialize draft
141
+ self.current_draft = MultiAgentMockDraft(user_pick_position=4)
142
+
143
+ # Run the appropriate draft
144
+ if use_a2a and self.a2a_manager:
145
+ yield from self.run_a2a_draft()
146
+ else:
147
+ # Use basic multiagent draft
148
+ draft_generator = run_interactive_mock_draft()
149
+
150
+ for output in draft_generator:
151
+ if isinstance(output, tuple):
152
+ # This means it's the user's turn
153
+ self.current_draft, self.draft_output = output
154
+ yield self.draft_output + "\n<!--USER_TURN-->"
155
+ return
156
+ else:
157
+ self.draft_output = output
158
+ yield output
159
+
160
+ def run_a2a_draft(self):
161
+ """Run draft with A2A communication."""
162
+ # Initialize draft
163
+ self.current_draft = MultiAgentMockDraft(user_pick_position=4)
164
+ self.draft_output = "# 🏈 Mock Draft with A2A Communication\n\n"
165
+
166
+ # Welcome message
167
+ if hasattr(self, 'a2a_type'):
168
+ if self.a2a_type == "full":
169
+ welcome_msg = "Welcome to the A2A-powered draft! Each agent is running on its own server with full A2A protocol."
170
+ elif self.a2a_type == "lightweight":
171
+ welcome_msg = "Welcome to the lightweight A2A draft! Each agent runs on its own HTTP server (no grpcio needed)."
172
+ else: # simulated
173
+ welcome_msg = "Welcome to the simulated A2A draft! Agents communicate using mock HTTP calls."
174
+ else:
175
+ welcome_msg = "Welcome to the A2A-powered draft! Each agent is running on its own server."
176
+ self.draft_output += format_agent_message(
177
+ "commissioner", "ALL",
178
+ welcome_msg
179
+ )
180
+ yield self.draft_output
181
+
182
+ # Run draft rounds
183
+ loop = asyncio.get_event_loop()
184
+
185
+ for round_num in range(1, 4): # 3 rounds
186
+ self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
187
+ yield self.draft_output
188
+
189
+ # Snake draft order
190
+ if round_num % 2 == 1:
191
+ pick_order = list(range(1, 7))
192
  else:
193
+ pick_order = list(range(6, 0, -1))
194
+
195
+ for pick_in_round, team_num in enumerate(pick_order, 1):
196
+ pick_num = (round_num - 1) * 6 + pick_in_round
197
+
198
+ # Show draft board at start of round
199
+ if pick_in_round == 1:
200
+ self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
201
+ self.draft_output += "\n"
202
+ yield self.draft_output
203
+
204
+ if team_num == 4: # User's turn
205
+ # Get advisor recommendation - use user_advisor directly
206
+ advisor = self.current_draft.user_advisor
207
+
208
+ # Get available players
209
+ all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
210
+ available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
211
+
212
+ # Get other agent strategies for advisor context
213
+ strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
214
+
215
+ # Get advisor recommendation
216
+ advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
217
+
218
+ # Show advisor message
219
+ self.draft_output += format_agent_message(advisor, "USER", advice)
220
+ yield self.draft_output
221
+
222
+ self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
223
+ yield self.draft_output + "\n<!--USER_TURN-->"
224
+ return
225
+ else:
226
+ # A2A agent pick
227
+ messages = loop.run_until_complete(
228
+ self.run_a2a_draft_turn(team_num, round_num, pick_num)
229
+ )
230
+
231
+ # Display messages with typing effect
232
+ for msg in messages:
233
+ if len(msg) >= 3:
234
+ agent, recipient, content = msg[:3]
235
+
236
+ # Show "..." first for typing effect
237
+ typing_placeholder = format_agent_message(agent, recipient, "...")
238
+ self.draft_output += typing_placeholder
239
+ yield self.draft_output
240
+ time.sleep(TYPING_DELAY_SECONDS)
241
+
242
+ # Replace with actual message
243
+ self.draft_output = self.draft_output.replace(typing_placeholder, "")
244
+ self.draft_output += format_agent_message(agent, recipient, content)
245
+ yield self.draft_output
246
+ time.sleep(MESSAGE_DELAY_SECONDS)
247
+
248
+ time.sleep(TYPING_DELAY_SECONDS)
249
+
250
+ # End of round
251
+ self.draft_output += format_agent_message("commissioner", "ALL",
252
+ f"That's the end of Round {round_num}!")
253
+ yield self.draft_output
254
+
255
+ # Final summary
256
+ self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
257
+ self.draft_output += self.current_draft.get_draft_summary()
258
+ yield self.draft_output
259
+
260
+ # Clear the draft state
261
+ self.current_draft = None
262
+
263
+ async def run_a2a_draft_turn(self, team_num: int, round_num: int, pick_num: int):
264
+ """Run a draft turn using A2A."""
265
+ messages = []
266
+
267
+ # Commissioner announcement
268
+ messages.append((
269
+ self.current_draft.commissioner,
270
+ "ALL",
271
+ f"Team {team_num} is on the clock!"
272
+ ))
273
+
274
+ # Get available players
275
+ all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
276
+ available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
277
+
278
+ # Get pick from A2A agent
279
+ previous_picks = self.current_draft.draft_board.get(team_num, [])
280
+ pick_result = await self.a2a_manager.get_pick(team_num, available, previous_picks, round_num)
281
+
282
+ if not pick_result or pick_result.type != "pick":
283
+ # Fallback to simulation
284
+ messages.append((
285
+ self.current_draft.commissioner,
286
+ "ALL",
287
+ f"⚠️ Team {team_num} A2A agent not responding - using simulation"
288
+ ))
289
+
290
+ sim_messages, _ = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
291
+ messages.extend(sim_messages)
292
+ return messages
293
+
294
+ # Make the pick
295
+ player = pick_result.player_name
296
+ self.current_draft.draft_board[team_num].append(player)
297
+
298
+ # Update agent's picks if it exists
299
+ agent = self.current_draft.agents.get(team_num)
300
+ if agent:
301
+ agent.picks.append(player)
302
+
303
+ # Commissioner announcement of pick
304
+ pick_num = len([p for picks in self.current_draft.draft_board.values() for p in picks])
305
+ confirm_msg = self.current_draft.commissioner.confirm_pick(
306
+ agent.team_name if agent else f"Team {team_num}",
307
+ player,
308
+ pick_num
309
+ )
310
+ messages.append((self.current_draft.commissioner, "ALL", confirm_msg))
311
+
312
+ # Agent explains reasoning
313
+ messages.append((
314
+ agent if agent else "system",
315
+ "ALL",
316
+ f"{pick_result.reasoning}"
317
+ ))
318
+
319
+ if pick_result.trash_talk:
320
+ messages.append((
321
+ agent if agent else "system",
322
+ "ALL",
323
+ pick_result.trash_talk
324
+ ))
325
+
326
+ # Get comments from other A2A agents (limit to 2 comments)
327
+ potential_commenters = [t for t in [1, 2, 3, 5, 6] if t != team_num and t != 4]
328
+
329
+ # Sort commenters to prioritize rivals
330
+ if team_num in RIVAL_PAIRS:
331
+ rivals = RIVAL_PAIRS[team_num]
332
+ if isinstance(rivals, int):
333
+ rivals = [rivals]
334
+ # Put rivals first in the list
335
+ prioritized_commenters = [t for t in rivals if t in potential_commenters]
336
+ prioritized_commenters.extend([t for t in potential_commenters if t not in prioritized_commenters])
337
+ potential_commenters = prioritized_commenters
338
+
339
+ # Collect comments up to the configured limit
340
+ comment_count = 0
341
+ max_comments = self.a2a_manager.max_comments_per_pick
342
+
343
+ for other_team in potential_commenters:
344
+ if comment_count >= max_comments:
345
+ break
346
+
347
+ comment = await self.a2a_manager.get_comment(other_team, team_num, player, round_num)
348
+ if comment:
349
+ other_agent = self.current_draft.agents.get(other_team)
350
+ if other_agent:
351
+ # Use the same pattern as earlier for the picking agent's name
352
+ picking_agent_name = agent.team_name if agent else f"Team {team_num}"
353
+ messages.append((
354
+ other_agent,
355
+ picking_agent_name,
356
+ comment
357
+ ))
358
+ comment_count += 1
359
+
360
+ return messages
361
 
362
  def continue_mock_draft(self, player_name: str):
363
  """Continue the mock draft after user makes a pick."""
 
379
 
380
  # Check if it's a typing indicator - skip it
381
  if isinstance(agent, str) and agent.startswith("typing_"):
382
+ continue
383
  else:
384
  # Show "..." first for typing effect
385
  typing_placeholder = format_agent_message(agent, recipient, "...")
386
  self.draft_output += typing_placeholder
387
  yield self.draft_output
388
+ time.sleep(TYPING_DELAY_SECONDS)
389
 
390
  # Replace "..." with actual message
391
  self.draft_output = self.draft_output.replace(typing_placeholder, "")
392
  self.draft_output += format_agent_message(agent, recipient, content)
393
  yield self.draft_output
394
+ time.sleep(MESSAGE_DELAY_SECONDS)
395
 
396
+ # Continue with the rest of the draft
397
+ if self.use_real_a2a and self.a2a_manager:
398
+ yield from self.continue_a2a_draft()
399
+ else:
400
+ yield from self.continue_basic_multiagent_draft()
401
+
402
+ def continue_a2a_draft(self):
403
+ """Continue A2A draft after user pick."""
404
+ # Calculate where we are
405
  total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
406
+ current_round = ((total_picks - 1) // 6) + 1
407
 
408
+ loop = asyncio.get_event_loop()
 
409
 
410
+ # Continue from current position
411
+ for round_num in range(current_round, 4):
412
  if round_num > current_round:
413
  self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
414
  yield self.draft_output
415
 
416
+ # Snake draft order
417
  if round_num % 2 == 1:
418
+ pick_order = list(range(1, 7))
419
  else:
420
+ pick_order = list(range(6, 0, -1))
421
 
422
  # Calculate where we are in the current round
423
+ picks_in_round = total_picks % 6
424
  start_idx = picks_in_round if round_num == current_round else 0
425
 
426
  for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1):
427
+ pick_num = (round_num - 1) * 6 + pick_in_round
428
 
429
  # Show draft board at start of round
430
  if pick_in_round == 1:
 
432
  self.draft_output += "\n"
433
  yield self.draft_output
434
 
435
+ if team_num == 4: # User's turn again
436
+ # Get advisor recommendation - use user_advisor directly
437
+ advisor = self.current_draft.user_advisor
438
+
439
+ all_picked = [p for picks in self.current_draft.draft_board.values() for p in picks]
440
+ available = [p for p in TOP_PLAYERS.keys() if p not in all_picked]
441
+
442
+ # Get other agent strategies for advisor context
443
+ strategies = {f"Team {i}": agent.strategy for i, agent in self.current_draft.agents.items()}
444
+
445
+ advice = advisor.advise_user(available, self.current_draft.draft_board, strategies)
446
+ self.draft_output += format_agent_message(advisor, "USER", advice)
447
+ yield self.draft_output
448
+
449
+ self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
450
+ yield self.draft_output + "\n<!--USER_TURN-->"
451
+ return
452
+ else:
453
+ # A2A agent pick
454
+ messages = loop.run_until_complete(
455
+ self.run_a2a_draft_turn(team_num, round_num, pick_num)
456
+ )
457
+
458
+ for msg in messages:
459
+ if len(msg) >= 3:
460
+ agent, recipient, content = msg[:3]
461
+ typing_placeholder = format_agent_message(agent, recipient, "...")
462
+ self.draft_output += typing_placeholder
463
+ yield self.draft_output
464
+ time.sleep(TYPING_DELAY_SECONDS)
465
+
466
+ self.draft_output = self.draft_output.replace(typing_placeholder, "")
467
+ self.draft_output += format_agent_message(agent, recipient, content)
468
+ yield self.draft_output
469
+ time.sleep(MESSAGE_DELAY_SECONDS)
470
+
471
+ time.sleep(TYPING_DELAY_SECONDS)
472
+
473
+ self.draft_output += format_agent_message("commissioner", "ALL",
474
+ f"That's the end of Round {round_num}!")
475
+ yield self.draft_output
476
+
477
+ # Final summary
478
+ self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
479
+ self.draft_output += self.current_draft.get_draft_summary()
480
+ yield self.draft_output
481
+
482
+ self.current_draft = None
483
+
484
+ def continue_basic_multiagent_draft(self):
485
+ """Continue basic multiagent draft after user pick."""
486
+ # This is the original logic from app.py
487
+ total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
488
+ current_round = ((total_picks - 1) // 6) + 1
489
+
490
+ draft_memories = []
491
+
492
+ for round_num in range(current_round, 4):
493
+ if round_num > current_round:
494
+ self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
495
+ yield self.draft_output
496
+
497
+ if round_num % 2 == 1:
498
+ pick_order = list(range(1, 7))
499
+ else:
500
+ pick_order = list(range(6, 0, -1))
501
+
502
+ picks_in_round = total_picks % 6
503
+ start_idx = picks_in_round if round_num == current_round else 0
504
+
505
+ for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1):
506
+ pick_num = (round_num - 1) * 6 + pick_in_round
507
+
508
+ if pick_in_round == 1:
509
+ self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
510
+ self.draft_output += "\n"
511
+ yield self.draft_output
512
+
513
  messages, result = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
514
 
 
515
  for msg in messages:
516
  if len(msg) >= 3:
517
  agent, recipient, content = msg[:3]
518
 
 
519
  if isinstance(agent, str) and agent.startswith("typing_"):
520
+ continue
521
  else:
 
522
  typing_placeholder = format_agent_message(agent, recipient, "...")
523
  self.draft_output += typing_placeholder
524
  yield self.draft_output
525
+ time.sleep(TYPING_DELAY_SECONDS)
526
 
 
527
  self.draft_output = self.draft_output.replace(typing_placeholder, "")
528
  self.draft_output += format_agent_message(agent, recipient, content)
529
  yield self.draft_output
530
+ time.sleep(MESSAGE_DELAY_SECONDS)
531
 
532
  if result is None:
 
533
  self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
534
  yield self.draft_output + "\n<!--USER_TURN-->"
535
  return
536
 
 
537
  if round_num > 1 and pick_in_round % 2 == 0:
538
  if team_num in self.current_draft.agents:
539
  agent = self.current_draft.agents[team_num]
 
545
  self.draft_output += format_memory_indicator(round_num, draft_memories[-2:])
546
  yield self.draft_output
547
 
548
+ time.sleep(TYPING_DELAY_SECONDS)
549
 
 
550
  self.draft_output += format_agent_message("commissioner", "ALL",
551
  f"That's the end of Round {round_num}!")
552
  yield self.draft_output
553
 
 
554
  self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
555
  self.draft_output += self.current_draft.get_draft_summary()
556
  yield self.draft_output
557
 
 
558
  self.current_draft = None
559
 
560
 
561
  def create_gradio_interface():
562
+ """Create the main Gradio interface with A2A support."""
 
563
 
564
+ with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Soft()) as demo:
565
+ # Create state for each user session
566
+ app_state = gr.State(None)
567
+
568
  with gr.Column(elem_id="main-container"):
569
  gr.Markdown("""
570
  # 🏈 Fantasy Draft Multi-Agent Demo
571
 
572
+ **Multi-agent system demo using the any-agent framework:** Watch 6 AI agents draft fantasy football teams while maintaining conversation history, reacting to each other's picks, and following distinct strategies.
573
  """)
574
 
575
  with gr.Tabs():
576
  # Demo Tab
577
  with gr.TabItem("🎮 Demo"):
578
+ # Add A2A Mode Toggle
579
+ with gr.Row():
580
+ with gr.Column():
581
+ gr.Markdown("### 🔧 Communication Mode")
582
+ communication_mode = gr.Radio(
583
+ ["Basic Multiagent", "A2A"],
584
+ value="Basic Multiagent",
585
+ label="Select how agents communicate",
586
+ info="Basic Multiagent: Fast, reliable (Recommended) | A2A: Distributed agents (Advanced)"
587
+ )
588
+ mode_info = gr.Markdown(
589
+ """
590
+ **Basic Multiagent** (Recommended): Fast, single-process execution (✅ Multi-user safe)
591
+ **A2A**: Distributed agents on HTTP servers (requires a2a-sdk package)
592
+
593
+ *If A2A mode fails to start, please use Basic Multiagent mode.*
594
+ """
595
+ )
596
+
597
+ # Add A2A test button for debugging
598
+ with gr.Accordion("🔧 A2A Debugging (Advanced)", open=False):
599
+ test_a2a_btn = gr.Button("Test A2A Dependencies & Ports", size="sm")
600
+ a2a_test_output = gr.Textbox(label="Test Results", lines=10, interactive=False)
601
+
602
+ # Show agent cards
603
  gr.Markdown("""
604
  ### 🏈 Meet Your Competition
605
 
 
612
  gr.Markdown("""
613
  <div style="background-color: #E3F2FD; border-left: 4px solid #1976D2; padding: 15px; border-radius: 8px;">
614
 
615
+ <h4 style="color: #0d47a1; margin: 0 0 10px 0;">📘🤓 Team 1 - Zero RB</h4>
616
 
617
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs get injured. I'll build around elite WRs."</p>
618
 
619
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
620
+ <li style="color: #424242;">Avoids RBs early</li>
621
+ <li style="color: #424242;">Loads up on WRs</li>
622
+ <li style="color: #424242;">Gets RB value late</li>
623
  </ul>
624
  </div>
625
  """)
 
628
  gr.Markdown("""
629
  <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
630
 
631
+ <h4 style="color: #1b5e20; margin: 0 0 10px 0;">📗🧑‍💼 Team 2 - BPA</h4>
632
 
633
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Value is value. I don't reach for needs."</p>
634
 
635
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
636
+ <li style="color: #424242;">Pure value drafting</li>
637
+ <li style="color: #424242;">Ignores needs</li>
638
+ <li style="color: #424242;">Mocks reaching</li>
639
  </ul>
640
  </div>
641
  """)
 
644
  gr.Markdown("""
645
  <div style="background-color: #FFF3E0; border-left: 4px solid #F57C00; padding: 15px; border-radius: 8px;">
646
 
647
+ <h4 style="color: #e65100; margin: 0 0 10px 0;">📙🧔 Team 3 - Robust RB</h4>
648
 
649
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs win championships. Period."</p>
650
 
651
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
652
+ <li style="color: #424242;">RBs in rounds 1-2</li>
653
+ <li style="color: #424242;">Old-school approach</li>
654
+ <li style="color: #424242;">Foundation first</li>
655
  </ul>
656
  </div>
657
  """)
 
660
  gr.Markdown("""
661
  <div style="background-color: #E8EAF6; border-left: 4px solid #3F51B5; padding: 15px; border-radius: 8px;">
662
 
663
+ <h4 style="color: #1a237e; margin: 0 0 10px 0;">👤 Position 4 - YOU</h4>
664
 
665
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">Your draft position with AI guidance</p>
666
 
667
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
668
+ <li style="color: #424242;">📕🧙 Strategic advisor</li>
669
+ <li style="color: #424242;">Real-time guidance</li>
670
+ <li style="color: #424242;">Roster analysis</li>
671
  </ul>
672
  </div>
673
  """)
 
676
  gr.Markdown("""
677
  <div style="background-color: #F5E6FF; border-left: 4px solid #7B1FA2; padding: 15px; border-radius: 8px;">
678
 
679
+ <h4 style="color: #4a148c; margin: 0 0 10px 0;">📓🤠 Team 5 - Upside</h4>
680
 
681
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Safe picks are for losers!"</p>
682
 
683
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
684
+ <li style="color: #424242;">Seeks breakouts</li>
685
+ <li style="color: #424242;">High risk/reward</li>
686
+ <li style="color: #424242;">Mocks safety</li>
687
  </ul>
688
  </div>
689
  """)
 
692
  gr.Markdown("""
693
  <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
694
 
695
+ <h4 style="color: #1b5e20; margin: 0 0 10px 0;">📗👨‍🏫 Team 6 - BPA</h4>
696
 
697
+ <p style="color: #424242; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Another value drafter to punish reaches."</p>
698
 
699
+ <ul style="color: #424242; font-size: 0.9em; margin: 0; padding-left: 20px;">
700
+ <li style="color: #424242;">Takes obvious value</li>
701
+ <li style="color: #424242;">Disciplined approach</li>
702
+ <li style="color: #424242;">No sentiment</li>
703
  </ul>
704
  </div>
705
  """)
706
 
707
  gr.Markdown("""
708
  ### 🎮 Draft Format
709
+ * **3 Rounds** of snake draft (1→6, 6→1, 1→6)
710
+ * **Real-time trash talk** between picks
711
+ * **Strategic advisor** guides your selections
712
+ * **Memory system** - agents remember and reference earlier picks
713
 
714
  Ready to experience the most realistic AI draft room?
715
  """)
 
764
 
765
  ### 💬 Agent-to-Agent (A2A) Communication
766
 
767
+ **Two Modes Available:**
768
+
769
+ #### 1. A2A Mode (Default)
770
+ - **Distributed Architecture**: Each agent runs on its own HTTP server
771
+ - **Dynamic Ports**: Each session gets unique ports automatically
772
+ - **True Isolation**: No shared memory, HTTP communication only
773
+ - **Production Ready**: Scalable to multiple machines
774
+ - **Uses a2a_tool_async**: Official any-agent A2A protocol
775
+
776
+ #### 2. Basic Multiagent Mode
777
+ - Single process, direct method calls
778
+ - Shared memory between agents
779
+ - Fast execution, simple debugging
780
+ - Perfect for quick testing and development
781
 
782
  ### 📊 Architecture Flow
783
  """)
 
826
  3. **Dynamic Adaptation**: Agents adjust based on draft progression
827
  4. **Natural Dialogue**: Human-like commentary and debates
828
  5. **User Integration**: Seamless human participation with AI guidance
829
+ 6. **A2A Communication**: Toggle between basic multiagent and distributed A2A modes
830
 
831
  ### 📝 Implementation Details
832
 
 
834
  - **Message Formatting**: Custom HTML/CSS for visual distinction
835
  - **State Management**: Draft board tracking and validation
836
  - **Memory Indicators**: Visual cues showing context retention
837
+ - **A2A Protocol**: Uses any-agent's a2a_tool_async for distributed communication
838
 
839
  ### 🚀 Why This Matters
840
 
841
  This demo proves that sophisticated multi-agent systems can be built with minimal code,
842
  showcasing the power of modern LLMs when properly orchestrated. The any-agent framework
843
  makes it easy to create agents that truly communicate and remember, not just respond.
844
+
845
+ The A2A mode demonstrates how the same agent logic can seamlessly transition from
846
+ a simple in-memory simulation to a production-ready distributed system.
847
  """)
848
 
849
  # Function to check if it's user's turn and show/hide controls
850
+ def check_user_turn(output_text, app):
851
  """Check if output indicates it's user's turn."""
852
  if "<!--USER_TURN-->" in output_text:
853
  # Remove the marker from display
854
  clean_output = output_text.replace("<!--USER_TURN-->", "")
855
  # Get available players
856
+ if app and app.current_draft:
857
  available = app.current_draft.get_available_players()
858
  available_text = "Available Players:\n\n"
859
  for player in sorted(available)[:20]: # Show top 20
 
879
  "" # Clear the input
880
  )
881
 
882
+ # Test A2A functionality
883
+ def test_a2a_functionality():
884
+ """Test A2A dependencies and port availability."""
885
+ import socket
886
+ import subprocess
887
+ import importlib.util
888
+ import site
889
+
890
+ test_results = []
891
+
892
+ # 1. Python Environment
893
+ test_results.append("=== Python Environment ===")
894
+ test_results.append(f"Python: {sys.version.split()[0]}")
895
+ test_results.append(f"Platform: {sys.platform}")
896
+ test_results.append(f"SPACE_ID: {os.getenv('SPACE_ID', 'Not on HF Spaces')}")
897
+
898
+ # 2. Check a2a-sdk installation
899
+ test_results.append("\n=== Package Installation ===")
900
+ try:
901
+ result = subprocess.run([sys.executable, "-m", "pip", "show", "a2a-sdk"],
902
+ capture_output=True, text=True, timeout=5)
903
+ if result.returncode == 0:
904
+ version_line = [line for line in result.stdout.split('\n') if line.startswith('Version:')]
905
+ location_line = [line for line in result.stdout.split('\n') if line.startswith('Location:')]
906
+ test_results.append(f"✅ a2a-sdk installed: {version_line[0] if version_line else 'Unknown version'}")
907
+ if location_line:
908
+ test_results.append(f" {location_line[0]}")
909
+ else:
910
+ test_results.append("❌ a2a-sdk NOT installed according to pip")
911
+ except Exception as e:
912
+ test_results.append(f"❌ Error checking pip: {e}")
913
+
914
+ # 3. Module search
915
+ test_results.append("\n=== Module Search ===")
916
+ a2a_spec = importlib.util.find_spec("a2a")
917
+ if a2a_spec:
918
+ test_results.append(f"✅ a2a module found at: {a2a_spec.origin}")
919
+ else:
920
+ test_results.append("❌ a2a module NOT found by importlib")
921
+ # Manual search
922
+ for path in site.getsitepackages():
923
+ if os.path.exists(path):
924
+ a2a_path = os.path.join(path, "a2a")
925
+ if os.path.exists(a2a_path):
926
+ test_results.append(f" Found a2a directory at: {a2a_path}")
927
+
928
+ # 4. Import tests
929
+ test_results.append("\n=== Import Tests ===")
930
+
931
+ # Basic a2a import
932
+ try:
933
+ import a2a
934
+ test_results.append(f"✅ import a2a: Success")
935
+ try:
936
+ import a2a.types
937
+ test_results.append("✅ import a2a.types: Success")
938
+ try:
939
+ from a2a.types import AgentSkill
940
+ test_results.append("✅ from a2a.types import AgentSkill: Success")
941
+ except ImportError as e:
942
+ test_results.append(f"❌ AgentSkill import: {e}")
943
+ except ImportError as e:
944
+ test_results.append(f"❌ a2a.types import: {e}")
945
+ except ImportError as e:
946
+ test_results.append(f"❌ a2a import failed: {e}")
947
+
948
+ # any_agent A2A imports
949
+ try:
950
+ from any_agent.serving import A2AServingConfig
951
+ from any_agent.tools import a2a_tool_async
952
+ test_results.append("✅ any_agent A2A components: Success!")
953
+ except ImportError as e:
954
+ test_results.append(f"❌ any_agent A2A import: {e}")
955
+
956
+ # 5. Port availability
957
+ test_results.append("\n=== Port Availability ===")
958
+ test_ports = [5001, 5002, 5003, 5004, 5005, 5006]
959
+ available_count = 0
960
+
961
+ for port in test_ports:
962
+ try:
963
+ sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
964
+ sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
965
+ sock.bind(('127.0.0.1', port))
966
+ test_results.append(f"✅ Port {port} available")
967
+ available_count += 1
968
+ sock.close()
969
+ except Exception:
970
+ test_results.append(f"❌ Port {port} not available")
971
+
972
+ test_results.append(f"\n📊 Summary: {available_count}/{len(test_ports)} ports available")
973
+
974
+ # 6. Try fixing a2a if needed
975
+ if "❌ a2a import failed" in "\n".join(test_results):
976
+ test_results.append("\n=== Attempting Fix ===")
977
+ try:
978
+ # Try reinstalling without deps
979
+ result = subprocess.run(
980
+ [sys.executable, "-m", "pip", "install", "a2a-sdk", "--no-deps", "--force-reinstall"],
981
+ capture_output=True, text=True, timeout=10
982
+ )
983
+ if result.returncode == 0:
984
+ test_results.append("✅ Reinstalled a2a-sdk")
985
+ # Test import again
986
+ try:
987
+ import importlib
988
+ if 'a2a' in sys.modules:
989
+ del sys.modules['a2a']
990
+ import a2a
991
+ test_results.append("✅ Import after reinstall: Success!")
992
+ except Exception as e:
993
+ test_results.append(f"❌ Import after reinstall: {e}")
994
+ else:
995
+ test_results.append(f"❌ Reinstall failed: {result.stderr[:200]}")
996
+ except Exception as e:
997
+ test_results.append(f"❌ Fix attempt error: {e}")
998
+
999
+ # Final verdict
1000
+ if available_count >= 6 and "✅ any_agent A2A components: Success!" in "\n".join(test_results):
1001
+ test_results.append("\n✅ A2A should work! Try selecting A2A mode.")
1002
+ else:
1003
+ test_results.append("\n❌ A2A requirements not met. Use Basic Multiagent mode.")
1004
+
1005
+ return "\n".join(test_results)
1006
+
1007
+ # No need for separate mode change handler - it happens when draft starts
1008
+
1009
  # Run multi-agent demo with control visibility handling
1010
+ def run_and_check(mode, app):
1011
  """Run demo and check for user turn."""
1012
+ # Create a new app instance for this user if needed
1013
+ if app is None:
1014
+ app = EnhancedFantasyDraftApp()
1015
+
1016
+ use_a2a = (mode == "A2A")
1017
+ for output in app.run_multiagent_demo(use_a2a):
1018
+ result = check_user_turn(output, app)
1019
+ yield result + (app,) # Return the app state as the last element
1020
 
1021
  run_multiagent_btn.click(
1022
  run_and_check,
1023
+ [communication_mode, app_state],
1024
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input, app_state],
1025
  show_progress=True
1026
  )
1027
 
1028
+ # Wire up A2A test button
1029
+ test_a2a_btn.click(
1030
+ test_a2a_functionality,
1031
+ [],
1032
+ [a2a_test_output]
1033
+ )
1034
+
1035
  # Continue draft after user pick
1036
+ def submit_and_continue(player_name, app):
1037
  """Submit pick and continue draft."""
1038
+ if app is None:
1039
+ yield ("No active draft. Please start a new mock draft.",
1040
+ gr.update(visible=False), gr.update(visible=False), "", "", None)
1041
+ return
1042
+
1043
  for output in app.continue_mock_draft(player_name):
1044
+ result = check_user_turn(output, app)
1045
+ yield result + (app,) # Return the app state as the last element
1046
 
1047
  submit_pick_btn.click(
1048
  submit_and_continue,
1049
+ [draft_pick_input, app_state],
1050
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input, app_state],
1051
  show_progress=True
1052
  )
1053
 
1054
  # Also submit on enter
1055
  draft_pick_input.submit(
1056
  submit_and_continue,
1057
+ [draft_pick_input, app_state],
1058
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input, app_state],
1059
  show_progress=True
1060
  )
1061
 
1062
+ # Minimal CSS for layout only
1063
  demo.css = """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  #main-container {
1065
+ max-width: 1200px;
1066
+ margin: 0 auto;
 
 
 
 
 
1067
  }
1068
 
1069
+ .multiagent-output {
1070
+ max-height: 800px;
1071
+ overflow-y: auto;
1072
  }
1073
 
1074
+ /* Force dark text in message cards */
1075
+ .multiagent-output div[style*="background-color"] {
1076
  color: #212121 !important;
1077
  }
1078
 
1079
+ .multiagent-output div[style*="background-color"] * {
 
 
 
 
 
 
 
 
 
1080
  color: #212121 !important;
1081
  }
1082
 
1083
+ #start-button {
1084
+ margin-top: 20px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1085
  }
1086
  """
1087
+
1088
+ # Note: Gradio's unload() doesn't support inputs, so automatic cleanup
1089
+ # happens when the Python process ends or when new sessions override old ones
1090
 
1091
  return demo
1092
 
1093
 
1094
  def main():
1095
+ """Main entry point."""
1096
+ # Check for API key - but don't exit on Hugging Face Spaces
 
 
 
 
 
1097
  if not os.getenv("OPENAI_API_KEY"):
1098
+ if os.getenv("SPACE_ID"): # Running on Hugging Face Spaces
1099
+ print("⚠️ OPENAI_API_KEY not found - please set it in Space Settings > Repository secrets")
1100
+ else:
1101
+ print("Error: OPENAI_API_KEY not found in environment")
1102
+ print("Please set it using: export OPENAI_API_KEY='your-key-here'")
1103
+ exit(1)
1104
 
1105
+ # Create and launch the interface
1106
+ demo = create_gradio_interface()
1107
 
1108
+ print("🚀 Launching Enhanced Fantasy Draft App with A2A Support...")
 
 
 
 
 
1109
 
1110
+ # Check if running on Hugging Face Spaces
1111
+ if os.getenv("SPACE_ID"):
1112
+ demo.launch() # Hugging Face handles server config
1113
+ else:
1114
+ demo.launch(
1115
+ server_name="0.0.0.0",
1116
+ server_port=7860,
1117
+ share=True,
1118
+ show_error=True
1119
+ )
1120
 
1121
 
1122
  if __name__ == "__main__":
apps/app_basic.py.bak ADDED
@@ -0,0 +1,700 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Fantasy Draft Multi-Agent Demo
4
+ Showcases multi-agent and multi-turn capabilities
5
+ """
6
+
7
+ import os
8
+ import time
9
+ import gradio as gr
10
+ from typing import List, Tuple
11
+ from dotenv import load_dotenv
12
+ import sys
13
+ import os
14
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
15
+
16
+ from core.agent import FantasyDraftAgent
17
+ from core.data import TOP_PLAYERS
18
+ from apps.multiagent_draft import MultiAgentMockDraft
19
+ from apps.multiagent_scenarios import (
20
+ run_interactive_mock_draft,
21
+ format_conversation_block,
22
+ format_agent_message,
23
+ format_memory_indicator,
24
+ create_mock_draft_visualization
25
+ )
26
+
27
+ # Fix for litellm 1.72.4 OpenAI endpoint issue
28
+ # This ensures litellm uses the correct OpenAI API endpoint
29
+ os.environ['OPENAI_API_BASE'] = 'https://api.openai.com/v1'
30
+
31
+ # Load environment variables from .env file
32
+ load_dotenv()
33
+
34
+
35
+ class FantasyDraftApp:
36
+ def __init__(self):
37
+ self.current_draft = None # Store the current mock draft
38
+ self.draft_output = "" # Store the draft output so far
39
+
40
+ def run_multiagent_demo(self):
41
+ """Run the mock draft demonstration."""
42
+ # Reset any previous draft
43
+ self.current_draft = None
44
+ self.draft_output = ""
45
+
46
+ # Run the draft generator
47
+ draft_generator = run_interactive_mock_draft()
48
+
49
+ for output in draft_generator:
50
+ # Check if this is a tuple (draft state, output)
51
+ if isinstance(output, tuple):
52
+ # This means it's the user's turn
53
+ self.current_draft, self.draft_output = output
54
+ # Add a special marker for Gradio to detect
55
+ yield self.draft_output + "\n<!--USER_TURN-->"
56
+ return
57
+ else:
58
+ # Regular output
59
+ self.draft_output = output
60
+ yield output
61
+
62
+ def continue_mock_draft(self, player_name: str):
63
+ """Continue the mock draft after user makes a pick."""
64
+ if not self.current_draft:
65
+ yield "No active draft. Please start a new mock draft."
66
+ return
67
+
68
+ if not player_name:
69
+ yield self.draft_output + "\n\n⚠️ Please enter a player name!"
70
+ return
71
+
72
+ # Make the user's pick
73
+ messages = self.current_draft.make_user_pick(player_name)
74
+
75
+ # Display messages with inline typing effect
76
+ for msg in messages:
77
+ if len(msg) >= 3:
78
+ agent, recipient, content = msg[:3]
79
+
80
+ # Check if it's a typing indicator - skip it
81
+ if isinstance(agent, str) and agent.startswith("typing_"):
82
+ continue # Skip typing indicators, we'll handle inline
83
+ else:
84
+ # Show "..." first for typing effect
85
+ typing_placeholder = format_agent_message(agent, recipient, "...")
86
+ self.draft_output += typing_placeholder
87
+ yield self.draft_output
88
+ time.sleep(0.5) # Brief typing delay
89
+
90
+ # Replace "..." with actual message
91
+ self.draft_output = self.draft_output.replace(typing_placeholder, "")
92
+ self.draft_output += format_agent_message(agent, recipient, content)
93
+ yield self.draft_output
94
+ time.sleep(1.0) # Reading delay
95
+
96
+ # Continue the draft from where we left off
97
+ # We need to track where we were in the draft
98
+ total_picks = len([p for picks in self.current_draft.draft_board.values() for p in picks])
99
+ current_round = ((total_picks - 1) // 6) + 1 # 6 teams per round
100
+
101
+ # Continue with the rest of the draft
102
+ draft_memories = []
103
+
104
+ # Continue the draft
105
+ for round_num in range(current_round, 4): # Continue from current round to round 3
106
+ if round_num > current_round:
107
+ self.draft_output += f"\n## 🔄 ROUND {round_num}\n\n"
108
+ yield self.draft_output
109
+
110
+ # Snake draft order - 6 teams total
111
+ if round_num % 2 == 1:
112
+ pick_order = list(range(1, 7)) # 1-6 for odd rounds
113
+ else:
114
+ pick_order = list(range(6, 0, -1)) # 6-1 for even rounds
115
+
116
+ # Calculate where we are in the current round
117
+ picks_in_round = total_picks % 6 # 6 teams per round
118
+ start_idx = picks_in_round if round_num == current_round else 0
119
+
120
+ for pick_in_round, team_num in enumerate(list(pick_order)[start_idx:], start_idx + 1):
121
+ pick_num = (round_num - 1) * 6 + pick_in_round # 6 teams per round
122
+
123
+ # Show draft board at start of round
124
+ if pick_in_round == 1:
125
+ self.draft_output += create_mock_draft_visualization(self.current_draft, round_num, pick_num)
126
+ self.draft_output += "\n"
127
+ yield self.draft_output
128
+
129
+ # Process the pick
130
+ messages, result = self.current_draft.simulate_draft_turn(round_num, pick_num, team_num)
131
+
132
+ # Display messages with inline typing effect
133
+ for msg in messages:
134
+ if len(msg) >= 3:
135
+ agent, recipient, content = msg[:3]
136
+
137
+ # Check if it's a typing indicator - skip it
138
+ if isinstance(agent, str) and agent.startswith("typing_"):
139
+ continue # Skip typing indicators, we'll handle inline
140
+ else:
141
+ # Show "..." first for typing effect
142
+ typing_placeholder = format_agent_message(agent, recipient, "...")
143
+ self.draft_output += typing_placeholder
144
+ yield self.draft_output
145
+ time.sleep(0.5) # Brief typing delay
146
+
147
+ # Replace "..." with actual message
148
+ self.draft_output = self.draft_output.replace(typing_placeholder, "")
149
+ self.draft_output += format_agent_message(agent, recipient, content)
150
+ yield self.draft_output
151
+ time.sleep(1.0) # Reading delay
152
+
153
+ if result is None:
154
+ # It's the user's turn again
155
+ self.draft_output += "\n**⏰ YOU'RE ON THE CLOCK! Type your pick below.**\n\n"
156
+ yield self.draft_output + "\n<!--USER_TURN-->"
157
+ return
158
+
159
+ # Add memory indicators
160
+ if round_num > 1 and pick_in_round % 2 == 0:
161
+ if team_num in self.current_draft.agents:
162
+ agent = self.current_draft.agents[team_num]
163
+ if len(agent.picks) > 1:
164
+ memory = f"{agent.team_name} has drafted: {', '.join(agent.picks)}"
165
+ draft_memories.append(memory)
166
+
167
+ if draft_memories:
168
+ self.draft_output += format_memory_indicator(round_num, draft_memories[-2:])
169
+ yield self.draft_output
170
+
171
+ time.sleep(0.5)
172
+
173
+ # End of round
174
+ self.draft_output += format_agent_message("commissioner", "ALL",
175
+ f"That's the end of Round {round_num}!")
176
+ yield self.draft_output
177
+
178
+ # Final summary
179
+ self.draft_output += "\n## 📊 FINAL RESULTS\n\n"
180
+ self.draft_output += self.current_draft.get_draft_summary()
181
+ yield self.draft_output
182
+
183
+ # Clear the draft state
184
+ self.current_draft = None
185
+
186
+
187
+ def create_gradio_interface():
188
+ """Create the main Gradio interface."""
189
+ app = FantasyDraftApp()
190
+
191
+ with gr.Blocks(title="Fantasy Draft Multi-Agent Demo", theme=gr.themes.Glass()) as demo:
192
+ with gr.Column(elem_id="main-container"):
193
+ gr.Markdown("""
194
+ # 🏈 Fantasy Draft Multi-Agent Demo
195
+
196
+ **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.
197
+ """)
198
+
199
+ with gr.Tabs():
200
+ # Demo Tab
201
+ with gr.TabItem("🎮 Demo"):
202
+ # Show agent cards first
203
+ gr.Markdown("""
204
+ ### 🏈 Meet Your Competition
205
+
206
+ You'll be drafting at **Position 4** with these AI opponents:
207
+ """)
208
+
209
+ # Agent cards in a grid - all in one row
210
+ with gr.Row():
211
+ with gr.Column(scale=1):
212
+ gr.Markdown("""
213
+ <div style="background-color: #E3F2FD; border-left: 4px solid #1976D2; padding: 15px; border-radius: 8px;">
214
+
215
+ <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">📘🤓 Team 1 - Zero RB</h4>
216
+
217
+ <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>
218
+
219
+ <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
220
+ <li style="color: #1a237e !important;">Avoids RBs early</li>
221
+ <li style="color: #1a237e !important;">Loads up on WRs</li>
222
+ <li style="color: #1a237e !important;">Gets RB value late</li>
223
+ </ul>
224
+ </div>
225
+ """)
226
+
227
+ with gr.Column(scale=1):
228
+ gr.Markdown("""
229
+ <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
230
+
231
+ <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗🧑‍💼 Team 2 - BPA</h4>
232
+
233
+ <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>
234
+
235
+ <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
236
+ <li style="color: #1b5e20 !important;">Pure value drafting</li>
237
+ <li style="color: #1b5e20 !important;">Ignores needs</li>
238
+ <li style="color: #1b5e20 !important;">Mocks reaching</li>
239
+ </ul>
240
+ </div>
241
+ """)
242
+
243
+ with gr.Column(scale=1):
244
+ gr.Markdown("""
245
+ <div style="background-color: #FFF3E0; border-left: 4px solid #F57C00; padding: 15px; border-radius: 8px;">
246
+
247
+ <h4 style="color: #e65100 !important; margin: 0 0 10px 0;">📙🧔 Team 3 - Robust RB</h4>
248
+
249
+ <p style="color: #ef6c00 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"RBs win championships. Period."</p>
250
+
251
+ <ul style="color: #e65100 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
252
+ <li style="color: #e65100 !important;">RBs in rounds 1-2</li>
253
+ <li style="color: #e65100 !important;">Old-school approach</li>
254
+ <li style="color: #e65100 !important;">Foundation first</li>
255
+ </ul>
256
+ </div>
257
+ """)
258
+
259
+ with gr.Column(scale=1):
260
+ gr.Markdown("""
261
+ <div style="background-color: #E8EAF6; border-left: 4px solid #3F51B5; padding: 15px; border-radius: 8px;">
262
+
263
+ <h4 style="color: #1a237e !important; margin: 0 0 10px 0;">👤 Position 4 - YOU</h4>
264
+
265
+ <p style="color: #3949ab !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">Your draft position with AI guidance</p>
266
+
267
+ <ul style="color: #1a237e !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
268
+ <li style="color: #1a237e !important;">📕🧙 Strategic advisor</li>
269
+ <li style="color: #1a237e !important;">Real-time guidance</li>
270
+ <li style="color: #1a237e !important;">Roster analysis</li>
271
+ </ul>
272
+ </div>
273
+ """)
274
+
275
+ with gr.Column(scale=1):
276
+ gr.Markdown("""
277
+ <div style="background-color: #F5E6FF; border-left: 4px solid #7B1FA2; padding: 15px; border-radius: 8px;">
278
+
279
+ <h4 style="color: #4a148c !important; margin: 0 0 10px 0;">📓🤠 Team 5 - Upside</h4>
280
+
281
+ <p style="color: #6a1b9a !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Safe picks are for losers!"</p>
282
+
283
+ <ul style="color: #4a148c !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
284
+ <li style="color: #4a148c !important;">Seeks breakouts</li>
285
+ <li style="color: #4a148c !important;">High risk/reward</li>
286
+ <li style="color: #4a148c !important;">Mocks safety</li>
287
+ </ul>
288
+ </div>
289
+ """)
290
+
291
+ with gr.Column(scale=1):
292
+ gr.Markdown("""
293
+ <div style="background-color: #E8F5E9; border-left: 4px solid #388E3C; padding: 15px; border-radius: 8px;">
294
+
295
+ <h4 style="color: #1b5e20 !important; margin: 0 0 10px 0;">📗👨‍🏫 Team 6 - BPA</h4>
296
+
297
+ <p style="color: #2e7d32 !important; font-style: italic; margin: 10px 0; font-size: 0.95em;">"Another value drafter to punish reaches."</p>
298
+
299
+ <ul style="color: #1b5e20 !important; font-size: 0.9em; margin: 0; padding-left: 20px;">
300
+ <li style="color: #1b5e20 !important;">Takes obvious value</li>
301
+ <li style="color: #1b5e20 !important;">Disciplined approach</li>
302
+ <li style="color: #1b5e20 !important;">No sentiment</li>
303
+ </ul>
304
+ </div>
305
+ """)
306
+
307
+ gr.Markdown("""
308
+ ### 🎮 Draft Format
309
+ - **3 Rounds** of snake draft (1→6, 6→1, 1→6)
310
+ - **Real-time trash talk** between picks
311
+ - **Strategic advisor** guides your selections
312
+ - **Memory system** - agents remember and reference earlier picks
313
+
314
+ Ready to experience the most realistic AI draft room?
315
+ """)
316
+
317
+ # Start button at the bottom
318
+ with gr.Row():
319
+ with gr.Column():
320
+ run_multiagent_btn = gr.Button("🏈 Start Mock Draft", variant="primary", size="lg", elem_id="start-button")
321
+
322
+ # Main output area
323
+ multiagent_output = gr.Markdown(elem_classes=["multiagent-output"])
324
+
325
+ # Mock draft interaction (hidden until needed)
326
+ with gr.Row(visible=False) as mock_draft_controls:
327
+ with gr.Column():
328
+ draft_pick_input = gr.Textbox(
329
+ label="Your Pick",
330
+ placeholder="Type player name and press Enter (e.g., 'Justin Jefferson')",
331
+ elem_id="draft-pick-input"
332
+ )
333
+ submit_pick_btn = gr.Button("Submit Pick", variant="primary")
334
+
335
+ # Available players display
336
+ with gr.Accordion("📋 Available Players", visible=False) as available_accordion:
337
+ available_players_display = gr.Textbox(
338
+ label="Top 20 Available",
339
+ lines=15,
340
+ interactive=False
341
+ )
342
+
343
+ # How It Works Tab
344
+ with gr.TabItem("🔧 How It Works"):
345
+ gr.Markdown("""
346
+ ## Technical Implementation
347
+
348
+ This demo showcases advanced multi-agent capabilities using the **any-agent framework**.
349
+
350
+ ### 🤖 Framework: any-agent (TinyAgent)
351
+
352
+ - **Lightweight**: < 100 lines of core agent code
353
+ - **Flexible**: Supports multiple LLM providers (OpenAI, Anthropic, etc.)
354
+ - **Multi-turn ready**: Built-in conversation history management
355
+ - **Model**: GPT-4 (configurable)
356
+
357
+ ### 🧠 Multi-Turn Memory System
358
+
359
+ Each agent maintains:
360
+ - **Conversation History**: Full context of all interactions
361
+ - **Draft State**: Current picks, available players, round info
362
+ - **Strategy Memory**: Remembers own strategy and others' approaches
363
+ - **Pick History**: Tracks all selections for informed decisions
364
+
365
+ ### 💬 Agent-to-Agent (A2A) Communication
366
+
367
+ Agents can:
368
+ - **Comment on picks**: React to other agents' selections
369
+ - **Respond to comments**: Defend their strategies
370
+ - **Remember debates**: Reference earlier conversations
371
+ - **Adapt strategies**: Adjust based on draft flow
372
+
373
+ ### 📊 Architecture Flow
374
+ """)
375
+
376
+ gr.Markdown("""
377
+ #### 1️⃣ INITIALIZATION
378
+ User clicks "Start Mock Draft" → System creates 6 agents
379
+
380
+ #### 2️⃣ AGENT SETUP
381
+ - **Team 1**: Zero RB Strategy
382
+ - **Team 2**: Best Player Available
383
+ - **Team 3**: Robust RB Strategy
384
+ - **YOU**: Position 4 (with Advisor)
385
+ - **Team 5**: Upside Hunter
386
+ - **Team 6**: Best Player Available
387
+
388
+ #### 3️⃣ DRAFT FLOW (3 Rounds)
389
+ - **Round 1**: Pick Order 1���2→3→YOU→5→6
390
+ - **Round 2**: Pick Order 6→5→YOU→3→2→1 (Snake)
391
+ - **Round 3**: Pick Order 1→2→3→YOU→5→6
392
+
393
+ #### 4️⃣ EACH PICK TRIGGERS
394
+ - Agent makes selection based on strategy
395
+ - Other agents comment (A2A communication)
396
+ - Original agent may respond
397
+ - All agents update their memory
398
+
399
+ #### 5️⃣ USER'S TURN
400
+ - Advisor analyzes draft state
401
+ - User sees available players
402
+ - User makes pick
403
+ - All agents react to user's choice
404
+
405
+ #### 6️⃣ MEMORY & CONTEXT
406
+ - Each agent remembers all picks
407
+ - Agents reference earlier conversations
408
+ - Strategies adapt based on draft flow
409
+ - Visual memory indicators show retention
410
+ """)
411
+
412
+ gr.Markdown("""
413
+ ### 🎯 Key Features Demonstrated
414
+
415
+ 1. **Persistent Context**: Each agent remembers all previous interactions
416
+ 2. **Strategic Personalities**: 5 distinct draft strategies competing
417
+ 3. **Dynamic Adaptation**: Agents adjust based on draft progression
418
+ 4. **Natural Dialogue**: Human-like commentary and debates
419
+ 5. **User Integration**: Seamless human participation with AI guidance
420
+
421
+ ### 📝 Implementation Details
422
+
423
+ - **Agent Classes**: Inheritance-based design with base `DraftAgent`
424
+ - **Message Formatting**: Custom HTML/CSS for visual distinction
425
+ - **State Management**: Draft board tracking and validation
426
+ - **Memory Indicators**: Visual cues showing context retention
427
+
428
+ ### 🚀 Why This Matters
429
+
430
+ This demo proves that sophisticated multi-agent systems can be built with minimal code,
431
+ showcasing the power of modern LLMs when properly orchestrated. The any-agent framework
432
+ makes it easy to create agents that truly communicate and remember, not just respond.
433
+ """)
434
+
435
+ # Function to check if it's user's turn and show/hide controls
436
+ def check_user_turn(output_text):
437
+ """Check if output indicates it's user's turn."""
438
+ if "<!--USER_TURN-->" in output_text:
439
+ # Remove the marker from display
440
+ clean_output = output_text.replace("<!--USER_TURN-->", "")
441
+ # Get available players
442
+ if app.current_draft:
443
+ available = app.current_draft.get_available_players()
444
+ available_text = "Available Players:\n\n"
445
+ for player in sorted(available)[:20]: # Show top 20
446
+ if player in TOP_PLAYERS:
447
+ info = TOP_PLAYERS[player]
448
+ available_text += f"• {player} ({info['pos']}, {info['team']})\n"
449
+ else:
450
+ available_text = "No draft active"
451
+
452
+ return (
453
+ clean_output, # Clean output
454
+ gr.update(visible=True), # Show draft controls
455
+ gr.update(visible=True, open=True), # Show available players and open it
456
+ available_text, # Available players list
457
+ "" # Clear the input
458
+ )
459
+ else:
460
+ return (
461
+ output_text, # Regular output
462
+ gr.update(visible=False), # Hide draft controls
463
+ gr.update(visible=False), # Hide available players
464
+ "", # Clear available list
465
+ "" # Clear the input
466
+ )
467
+
468
+ # Run multi-agent demo with control visibility handling
469
+ def run_and_check():
470
+ """Run demo and check for user turn."""
471
+ for output in app.run_multiagent_demo():
472
+ result = check_user_turn(output)
473
+ yield result
474
+
475
+ run_multiagent_btn.click(
476
+ run_and_check,
477
+ None,
478
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
479
+ show_progress=True
480
+ )
481
+
482
+ # Continue draft after user pick
483
+ def submit_and_continue(player_name):
484
+ """Submit pick and continue draft."""
485
+ for output in app.continue_mock_draft(player_name):
486
+ result = check_user_turn(output)
487
+ yield result
488
+
489
+ submit_pick_btn.click(
490
+ submit_and_continue,
491
+ draft_pick_input,
492
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
493
+ show_progress=True
494
+ )
495
+
496
+ # Also submit on enter
497
+ draft_pick_input.submit(
498
+ submit_and_continue,
499
+ draft_pick_input,
500
+ [multiagent_output, mock_draft_controls, available_accordion, available_players_display, draft_pick_input],
501
+ show_progress=True
502
+ )
503
+
504
+ # Add custom CSS for better styling
505
+ demo.css = """
506
+ /* Force white text on dark background for all main content */
507
+ .gradio-container {
508
+ max-width: 800px !important;
509
+ margin: 0 auto !important;
510
+ color: white !important;
511
+ }
512
+
513
+ /* Ensure all text elements are white by default */
514
+ .gradio-container p,
515
+ .gradio-container h1,
516
+ .gradio-container h2,
517
+ .gradio-container h3,
518
+ .gradio-container h4,
519
+ .gradio-container h5,
520
+ .gradio-container h6,
521
+ .gradio-container span,
522
+ .gradio-container div,
523
+ .gradio-container label,
524
+ .gradio-container .markdown,
525
+ .gradio-container .prose {
526
+ color: white !important;
527
+ }
528
+
529
+ /* Ensure markdown content is white */
530
+ .markdown-text,
531
+ .markdown-text p,
532
+ .markdown-text li,
533
+ .markdown-text ul,
534
+ .markdown-text ol {
535
+ color: white !important;
536
+ }
537
+
538
+ #main-container {
539
+ text-align: center;
540
+ color: white !important;
541
+ }
542
+
543
+ /* Left-align text in How It Works tab */
544
+ .tabitem:nth-child(2) {
545
+ text-align: left !important;
546
+ }
547
+
548
+ #start-button {
549
+ margin: 20px auto !important;
550
+ max-width: 300px !important;
551
+ }
552
+
553
+ /* Only force dark text inside colored message boxes */
554
+ div[style*="background-color"][style*="border-left"] {
555
+ color: #212121 !important;
556
+ }
557
+
558
+ div[style*="background-color"][style*="border-left"] p,
559
+ div[style*="background-color"][style*="border-left"] strong,
560
+ div[style*="background-color"][style*="border-left"] em,
561
+ div[style*="background-color"][style*="border-left"] span,
562
+ div[style*="background-color"][style*="border-left"] li,
563
+ div[style*="background-color"][style*="border-left"] ul,
564
+ div[style*="background-color"][style*="border-left"] h1,
565
+ div[style*="background-color"][style*="border-left"] h2,
566
+ div[style*="background-color"][style*="border-left"] h3,
567
+ div[style*="background-color"][style*="border-left"] h4 {
568
+ color: #212121 !important;
569
+ }
570
+
571
+ /* System messages with yellow background */
572
+ div[style*="#FFF9C4"] {
573
+ color: #F57C00 !important;
574
+ }
575
+
576
+ /* Memory boxes */
577
+ div[style*="#F5F5F5"] {
578
+ color: #424242 !important;
579
+ }
580
+
581
+ #draft-pick-input {
582
+ font-size: 1.1em;
583
+ }
584
+
585
+ /* Ensure tab labels are visible */
586
+ .tab-nav button {
587
+ color: white !important;
588
+ }
589
+
590
+ /* Ensure multiagent output text is white */
591
+ .multiagent-output {
592
+ color: white !important;
593
+ }
594
+
595
+ .multiagent-output p,
596
+ .multiagent-output h1,
597
+ .multiagent-output h2,
598
+ .multiagent-output h3,
599
+ .multiagent-output h4,
600
+ .multiagent-output h5,
601
+ .multiagent-output h6,
602
+ .multiagent-output span,
603
+ .multiagent-output div {
604
+ color: white !important;
605
+ }
606
+
607
+ /* Specific rules for dark mode - target Gradio's dark theme class */
608
+ .dark .gradio-container,
609
+ .dark .gradio-container *:not([style*="background-color"]) {
610
+ color: white !important;
611
+ }
612
+
613
+ /* Ensure description text under title is white */
614
+ .gradio-container > div > div > div > p {
615
+ color: white !important;
616
+ }
617
+
618
+ /* Tab content text */
619
+ .tabitem .markdown-text {
620
+ color: white !important;
621
+ }
622
+
623
+ /* Input labels and text */
624
+ .gradio-container label {
625
+ color: rgba(255, 255, 255, 0.9) !important;
626
+ }
627
+
628
+ /* Button text that's not in primary buttons */
629
+ button:not(.primary) {
630
+ color: rgba(255, 255, 255, 0.9) !important;
631
+ }
632
+
633
+ /* Fix for bold player names - ensure they're visible */
634
+ .multiagent-output strong,
635
+ .multiagent-output b {
636
+ color: inherit !important;
637
+ font-weight: 700;
638
+ }
639
+
640
+ /* Ensure bold text in message backgrounds has proper color */
641
+ div[style*="background-color"] strong,
642
+ div[style*="background-color"] b {
643
+ color: inherit !important;
644
+ }
645
+
646
+ /* Specific fix for bold text in different message backgrounds */
647
+ div[style*="background-color: #E3F2FD"] strong, /* Team messages */
648
+ div[style*="background-color: #FFF8E1"] strong, /* Commissioner */
649
+ div[style*="background-color: #FFEBEE"] strong, /* Advisor */
650
+ div[style*="background-color: #F3E5F5"] strong { /* Memory */
651
+ color: #1a1a1a !important;
652
+ }
653
+
654
+ /* Inline typing effect */
655
+ .typing-dots {
656
+ animation: pulse 1.0s ease-in-out infinite;
657
+ }
658
+
659
+ @keyframes pulse {
660
+ 0%, 100% { opacity: 0.6; }
661
+ 50% { opacity: 1; }
662
+ }
663
+ """
664
+
665
+ return demo
666
+
667
+
668
+ def main():
669
+ """Launch the Gradio app."""
670
+ import sys
671
+
672
+ # Check for --share flag
673
+ share_mode = "--share" in sys.argv or "-s" in sys.argv
674
+
675
+ # Check for API key
676
+ if not os.getenv("OPENAI_API_KEY"):
677
+ print("\n⚠️ Warning: OPENAI_API_KEY not found in environment variables.")
678
+ print("The app will launch but agent responses will fail without an API key.")
679
+ print("Set it using: export OPENAI_API_KEY='your-key-here'\n")
680
+
681
+ print("🚀 Launching Fantasy Draft Multi-Agent Demo...")
682
+
683
+ if share_mode:
684
+ print("🌐 Creating public share link (expires in 72 hours)...")
685
+ print("📡 Please wait for the public URL...\n")
686
+ else:
687
+ print("📡 The app will be available at http://localhost:7860")
688
+ print("💡 Tip: Use 'python app.py --share' to create a public link\n")
689
+
690
+ demo = create_gradio_interface()
691
+ demo.launch(
692
+ server_name="0.0.0.0",
693
+ server_port=7860,
694
+ share=share_mode, # Enable sharing if flag is present
695
+ show_error=True
696
+ )
697
+
698
+
699
+ if __name__ == "__main__":
700
+ main()
core/dynamic_a2a_manager.py CHANGED
@@ -27,7 +27,7 @@ from core.a2a_helpers import (
27
  )
28
 
29
 
30
- # A2A Output model (same as in app_enhanced.py)
31
  class A2AOutput(BaseModel):
32
  """Combined output type for A2A agents."""
33
  type: str # "pick" or "comment"
 
27
  )
28
 
29
 
30
+ # A2A Output model (same as in apps/app.py)
31
  class A2AOutput(BaseModel):
32
  """Combined output type for A2A agents."""
33
  type: str # "pick" or "comment"
docs/FEATURES_AND_ENHANCEMENTS.md CHANGED
@@ -166,7 +166,7 @@ AGENT_START_DELAY = 0.5 # A2A startup spacing
166
  - Adjust delays in `constants.py`
167
  - Modify agent strategies in `agent.py`
168
  - Add players to `data.py`
169
- - Customize UI in `app_enhanced.py`
170
 
171
  ## Future Possibilities
172
  - WebSocket real-time updates
 
166
  - Adjust delays in `constants.py`
167
  - Modify agent strategies in `agent.py`
168
  - Add players to `data.py`
169
+ - Customize UI in `apps/app.py`
170
 
171
  ## Future Possibilities
172
  - WebSocket real-time updates
docs/SETUP_GUIDE.md CHANGED
@@ -25,7 +25,7 @@ This guide covers everything you need to set up and run the Fantasy Draft Multi-
25
 
26
  4. **Run the app**
27
  ```bash
28
- python apps/app_enhanced.py
29
  ```
30
 
31
  ## Detailed Setup
@@ -69,13 +69,13 @@ deactivate
69
  2. **Port Configuration**:
70
  - Default web interface: Port 7860
71
  - A2A mode uses dynamic ports (5000-9000 range)
72
- - Modify in `app_enhanced.py` if needed
73
 
74
  ## Running Different Modes
75
 
76
  ### Basic Mode (Recommended for Development)
77
  ```bash
78
- python apps/app_enhanced.py
79
  ```
80
  - Single process, fast execution
81
  - Good for testing and development
@@ -117,7 +117,7 @@ python apps/app_enhanced.py
117
  - Verify key starts with `sk-`
118
 
119
  2. **Port already in use**
120
- - Change port in `app_enhanced.py`: `demo.launch(server_port=7861)`
121
  - Or kill the process using the port
122
 
123
  3. **Module not found errors**
 
25
 
26
  4. **Run the app**
27
  ```bash
28
+ python apps/app.py
29
  ```
30
 
31
  ## Detailed Setup
 
69
  2. **Port Configuration**:
70
  - Default web interface: Port 7860
71
  - A2A mode uses dynamic ports (5000-9000 range)
72
+ - Modify in `apps/app.py` if needed
73
 
74
  ## Running Different Modes
75
 
76
  ### Basic Mode (Recommended for Development)
77
  ```bash
78
+ python apps/app.py
79
  ```
80
  - Single process, fast execution
81
  - Good for testing and development
 
117
  - Verify key starts with `sk-`
118
 
119
  2. **Port already in use**
120
+ - Change port in `apps/app.py`: `demo.launch(server_port=7861)`
121
  - Or kill the process using the port
122
 
123
  3. **Module not found errors**
docs/TECHNICAL_DOCUMENTATION.md CHANGED
@@ -14,7 +14,7 @@ The Fantasy Draft Multi-Agent Demo showcases a sophisticated multi-agent system
14
  ```
15
  fantasy-draft-agent/
16
  ├── apps/
17
- │ ├── app_enhanced.py # Main Gradio interface with A2A support
18
  │ ├── multiagent_draft.py # Core draft logic and agent management
19
  │ └── multiagent_scenarios.py # UI formatting and visualization
20
  ├── core/
 
14
  ```
15
  fantasy-draft-agent/
16
  ├── apps/
17
+ │ ├── app.py # Main Gradio interface with A2A support
18
  │ ├── multiagent_draft.py # Core draft logic and agent management
19
  │ └── multiagent_scenarios.py # UI formatting and visualization
20
  ├── core/