WhoDat55 commited on
Commit
672aa93
·
verified ·
1 Parent(s): 8cdc53d

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +478 -0
app.py ADDED
@@ -0,0 +1,478 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ import os
5
+ import time
6
+ from configparser import ConfigParser
7
+ from typing import List, Dict, Any
8
+
9
+ MCP_SERVER_URL = "https://whodat55-hackathon-core-mcp.hf.space/execute"
10
+
11
+ # HARDCODED PROVIDER MAPPING (Directive 1.1, 1.2, 1.3)
12
+ PROVIDER_MODEL_MAPPING = {
13
+ "Google Gemini": [],
14
+ "Anthropic": [],
15
+ "OpenAI": [],
16
+ "Custom": []
17
+ }
18
+
19
+ def classify_model_by_provider(model_label: str) -> str:
20
+ """Classify a model label into a provider category using keyword matching"""
21
+ label_lower = model_label.lower()
22
+
23
+ if 'gemini' in label_lower:
24
+ return "Google Gemini"
25
+ elif 'claude' in label_lower or 'anthropic' in label_lower:
26
+ return "Anthropic"
27
+ elif 'gpt' in label_lower or 'openai' in label_lower:
28
+ return "OpenAI"
29
+ else:
30
+ return "Custom"
31
+
32
+ def load_client_config():
33
+ config = {}
34
+ coder_models = {}
35
+ strategist_models = {}
36
+
37
+ if os.path.exists('config.ini'):
38
+ parser = ConfigParser()
39
+ parser.read('config.ini')
40
+ try:
41
+ if 'CODER_MODELS' in parser:
42
+ for label, entry in parser['CODER_MODELS'].items():
43
+ coder_models[label] = label
44
+ if 'STRATEGIST_MODELS' in parser:
45
+ for label, entry in parser['STRATEGIST_MODELS'].items():
46
+ strategist_models[label] = label
47
+ except Exception as e:
48
+ print(f"Config parsing warning: {e}")
49
+
50
+ # Build provider-grouped structure
51
+ coder_by_provider = {p: [] for p in PROVIDER_MODEL_MAPPING.keys()}
52
+ strategist_by_provider = {p: [] for p in PROVIDER_MODEL_MAPPING.keys()}
53
+
54
+ for label in coder_models.keys():
55
+ provider = classify_model_by_provider(label)
56
+ coder_by_provider[provider].append(label)
57
+
58
+ for label in strategist_models.keys():
59
+ provider = classify_model_by_provider(label)
60
+ strategist_by_provider[provider].append(label)
61
+
62
+ config['coder_by_provider'] = coder_by_provider
63
+ config['strategist_by_provider'] = strategist_by_provider
64
+ config['coder_models'] = list(coder_models.keys())
65
+ config['strategist_models'] = list(strategist_models.keys())
66
+ config['coder_default'] = os.getenv('CODER_MODEL_DEFAULT_LABEL', '')
67
+ config['strategist_default'] = os.getenv('STRATEGIST_MODEL_DEFAULT_LABEL', '')
68
+
69
+ return config
70
+
71
+ CLIENT_CONFIG = load_client_config()
72
+
73
+ def format_conversation_log(history: List[Dict[str, Any]]) -> str:
74
+ log_lines = []
75
+ for i, msg in enumerate(history):
76
+ role_label = msg.get('role', 'UNKNOWN').upper()
77
+ text_content = ""
78
+ if 'parts' in msg and isinstance(msg['parts'], list):
79
+ text_content = msg['parts'][0].get('text', '')
80
+
81
+ log_lines.append(f"{'='*80}")
82
+ log_lines.append(f"[{i}] {role_label}")
83
+ log_lines.append(f"{'='*80}")
84
+ log_lines.append(text_content)
85
+ log_lines.append("")
86
+ return "\n".join(log_lines)
87
+
88
+ def build_master_log(system_log_list, coder_log_list, strategist_log_list, audit_log_list, start_time):
89
+ actor_styles = {
90
+ 'SYSTEM': {'icon': '🔧', 'color': '#ef4444'},
91
+ 'CODER': {'icon': '💻', 'color': '#3b82f6'},
92
+ 'STRATEGIST': {'icon': '🎯', 'color': '#a855f7'},
93
+ 'TOOL': {'icon': '🛠️', 'color': '#f97316'}
94
+ }
95
+
96
+ master_entries = []
97
+ seq_counter = 1
98
+
99
+ for entry in system_log_list:
100
+ elapsed = time.time() - start_time
101
+ minutes = int(elapsed // 60)
102
+ seconds = int(elapsed % 60)
103
+ milliseconds = int((elapsed % 1) * 1000)
104
+ timestamp = f"{minutes:02d}:{seconds:02d}:{milliseconds:03d}"
105
+
106
+ style = actor_styles['SYSTEM']
107
+ html_entry = f'<div style="margin-bottom: 8px; font-family: monospace;"><span style="color: {style["color"]}; font-weight: bold;">{style["icon"]} [SEQ-{seq_counter:02d}] [{timestamp}] SYSTEM:</span> {entry}</div>'
108
+ master_entries.append(html_entry)
109
+ seq_counter += 1
110
+
111
+ for msg in coder_log_list:
112
+ elapsed = time.time() - start_time
113
+ minutes = int(elapsed // 60)
114
+ seconds = int(elapsed % 60)
115
+ milliseconds = int((elapsed % 1) * 1000)
116
+ timestamp = f"{minutes:02d}:{seconds:02d}:{milliseconds:03d}"
117
+
118
+ role = msg.get('role', 'UNKNOWN').upper()
119
+ actor = f"CODER ({role})"
120
+
121
+ text_content = ""
122
+ if 'parts' in msg and isinstance(msg['parts'], list):
123
+ text_content = msg['parts'][0].get('text', '')
124
+
125
+ style = actor_styles['CODER']
126
+ html_entry = f'<div style="margin-bottom: 8px; font-family: monospace;"><span style="color: {style["color"]}; font-weight: bold;">{style["icon"]} [SEQ-{seq_counter:02d}] [{timestamp}] {actor}:</span> {text_content}</div>'
127
+ master_entries.append(html_entry)
128
+ seq_counter += 1
129
+
130
+ for msg in strategist_log_list:
131
+ elapsed = time.time() - start_time
132
+ minutes = int(elapsed // 60)
133
+ seconds = int(elapsed % 60)
134
+ milliseconds = int((elapsed % 1) * 1000)
135
+ timestamp = f"{minutes:02d}:{seconds:02d}:{milliseconds:03d}"
136
+
137
+ role = msg.get('role', 'UNKNOWN').upper()
138
+ actor = f"STRATEGIST ({role})"
139
+
140
+ text_content = ""
141
+ if 'parts' in msg and isinstance(msg['parts'], list):
142
+ text_content = msg['parts'][0].get('text', '')
143
+
144
+ style = actor_styles['STRATEGIST']
145
+ html_entry = f'<div style="margin-bottom: 8px; font-family: monospace;"><span style="color: {style["color"]}; font-weight: bold;">{style["icon"]} [SEQ-{seq_counter:02d}] [{timestamp}] {actor}:</span> {text_content}</div>'
146
+ master_entries.append(html_entry)
147
+ seq_counter += 1
148
+
149
+ for diagnostic in audit_log_list:
150
+ elapsed = time.time() - start_time
151
+ minutes = int(elapsed // 60)
152
+ seconds = int(elapsed % 60)
153
+ milliseconds = int((elapsed % 1) * 1000)
154
+ timestamp = f"{minutes:02d}:{seconds:02d}:{milliseconds:03d}"
155
+
156
+ tool_name = diagnostic.get('tool', 'UNKNOWN')
157
+ status = diagnostic.get('status', 'UNKNOWN')
158
+ latency = diagnostic.get('latency', 'N/A')
159
+ transaction_id = diagnostic.get('transaction_id', 'N/A')
160
+
161
+ request_str = json.dumps(diagnostic.get('request'), indent=2)
162
+ response_str = json.dumps(diagnostic.get('response'), indent=2) if diagnostic.get('response') else "None"
163
+
164
+ tool_entry = f"TOOL: {tool_name} | TRANSACTION: {transaction_id} | STATUS: {status} | LATENCY: {latency}s<br>[REQUEST]<br><pre>{request_str}</pre><br>[RESPONSE]<br><pre>{response_str}</pre>"
165
+
166
+ style = actor_styles['TOOL']
167
+ html_entry = f'<div style="margin-bottom: 8px; font-family: monospace;"><span style="color: {style["color"]}; font-weight: bold;">{style["icon"]} [SEQ-{seq_counter:02d}] [{timestamp}] TOOL:</span> {tool_entry}</div>'
168
+ master_entries.append(html_entry)
169
+ seq_counter += 1
170
+
171
+ if not master_entries:
172
+ return '<div style="font-family: monospace; color: #6b7280;">No log data captured.</div>'
173
+
174
+ html_output = f'''
175
+ <div style="background-color: #1e1e1e; padding: 16px; border-radius: 8px; max-height: 600px; overflow-y: auto; color: #e0e0e0;">
176
+ {"".join(master_entries)}
177
+ </div>
178
+ '''
179
+
180
+ return html_output
181
+
182
+ def execute_remote_workflow(blueprint, intent, coder_model_label, strategist_model_label):
183
+ """Streaming workflow execution with real-time log updates"""
184
+ mission_start_time = time.time()
185
+
186
+ payload = {
187
+ "blueprint": blueprint,
188
+ "intent": intent,
189
+ "coder_model_label": coder_model_label,
190
+ "strategist_model_label": strategist_model_label
191
+ }
192
+
193
+ try:
194
+ target_url = MCP_SERVER_URL.replace("/execute", "/execute_stream")
195
+ print(f"DEBUG: Connecting to streaming endpoint: {target_url}")
196
+
197
+ with requests.post(target_url, json=payload, stream=True, timeout=300) as response:
198
+ if response.status_code == 404:
199
+ yield f"ERROR 404: Streaming endpoint not found at {target_url}. Check server version.", "", "", ""
200
+ return
201
+
202
+ response.raise_for_status()
203
+
204
+ for line in response.iter_lines():
205
+ if not line:
206
+ continue
207
+
208
+ decoded = line.decode('utf-8')
209
+ if not decoded.startswith('data: '):
210
+ continue
211
+
212
+ data_str = decoded[6:]
213
+
214
+ try:
215
+ data = json.loads(data_str)
216
+ except json.JSONDecodeError:
217
+ continue
218
+
219
+ if data.get("status") == "complete":
220
+ break
221
+
222
+ system_log_list = data.get("system_log", [])
223
+ coder_log_list = data.get("coder_log", [])
224
+ strategist_log_list = data.get("strategist_log", [])
225
+ audit_log_list = data.get("audit_log", [])
226
+
227
+ sys_log = "\n".join(system_log_list)
228
+ cod_log = format_conversation_log(coder_log_list)
229
+ strat_log = format_conversation_log(strategist_log_list)
230
+ master_log = build_master_log(system_log_list, coder_log_list,
231
+ strategist_log_list, audit_log_list,
232
+ mission_start_time)
233
+
234
+ yield sys_log, cod_log, strat_log, master_log
235
+
236
+ except requests.exceptions.RequestException as e:
237
+ error_detail = str(e)
238
+ if hasattr(e, 'response') and e.response is not None:
239
+ error_detail += f"\nServer Response: {e.response.text}"
240
+
241
+ error_msg = f"CONNECTION ERROR: Could not connect to MCP Server.\nDetails: {error_detail}"
242
+ yield error_msg, "", "", ""
243
+ except Exception as e:
244
+ error_msg = f"CLIENT ERROR: {str(e)}"
245
+ yield error_msg, "", "", ""
246
+
247
+ def create_interface():
248
+ DEMO_BLUEPRINT = """# Gradio Application Blueprint
249
+
250
+ Produce a pro level styled and tuned them GUI that has 1 button that when pressed displays a celebration announcement "Happy Birthday MCP!". USE specified colors or themes appropriate for the event. You must incorporate specific colors for design pieces and thematic layouts providing a fun and exciting announcement."""
251
+ DEMO_INTENT = """A simple test button with a clean look. That when pressed creates a simple happy birthday MCP response on the screen. The gui should be gradio and contain colors that are used in professional builds and match the Gradio, MCP or other color theme or as close as possible."""
252
+
253
+ # Get initial defaults
254
+ coder_default = CLIENT_CONFIG['coder_default']
255
+ strategist_default = CLIENT_CONFIG['strategist_default']
256
+
257
+ # Determine initial provider for each agent
258
+ initial_coder_provider = "Google Gemini"
259
+ initial_strategist_provider = "Google Gemini"
260
+
261
+ if coder_default:
262
+ initial_coder_provider = classify_model_by_provider(coder_default)
263
+
264
+ if strategist_default:
265
+ initial_strategist_provider = classify_model_by_provider(strategist_default)
266
+
267
+ def load_demo_data():
268
+ return DEMO_BLUEPRINT, DEMO_INTENT
269
+
270
+ def start_execution_sequence():
271
+ return gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)
272
+
273
+ def end_execution_sequence(sys_log, cod_log, strat_log, master_log):
274
+ return sys_log, cod_log, strat_log, master_log, gr.update(visible=False), gr.update(visible=True)
275
+
276
+ def reset_to_default():
277
+ return gr.update(visible=True), gr.update(visible=False), gr.update(visible=False)
278
+
279
+ # Cascading selector update functions
280
+ def update_coder_models(provider):
281
+ models = CLIENT_CONFIG['coder_by_provider'].get(provider, [])
282
+ if not models:
283
+ return gr.update(choices=[], value=None)
284
+ return gr.update(choices=models, value=models[0])
285
+
286
+ def update_strategist_models(provider):
287
+ models = CLIENT_CONFIG['strategist_by_provider'].get(provider, [])
288
+ if not models:
289
+ return gr.update(choices=[], value=None)
290
+ return gr.update(choices=models, value=models[0])
291
+
292
+ # RESET MISSION function (Directive 2.1)
293
+ def reset_mission():
294
+ """Total system reset: clear all fields and reset to defaults"""
295
+ # Determine initial models for reset
296
+ coder_models_for_reset = CLIENT_CONFIG['coder_by_provider'].get(initial_coder_provider, [])
297
+ strategist_models_for_reset = CLIENT_CONFIG['strategist_by_provider'].get(initial_strategist_provider, [])
298
+
299
+ coder_reset_value = coder_models_for_reset[0] if coder_models_for_reset else None
300
+ strategist_reset_value = strategist_models_for_reset[0] if strategist_models_for_reset else None
301
+
302
+ return (
303
+ "", # blueprint_input
304
+ "", # intent_input
305
+ "", # system_terminal
306
+ "", # coder_log
307
+ "", # strategist_log
308
+ '<div style="font-family: monospace; color: #6b7280;">*Mission reset complete. All systems cleared.*</div>', # master_log
309
+ gr.update(value=initial_coder_provider), # coder_provider
310
+ gr.update(choices=coder_models_for_reset, value=coder_reset_value), # coder_model
311
+ gr.update(value=initial_strategist_provider), # strategist_provider
312
+ gr.update(choices=strategist_models_for_reset, value=strategist_reset_value) # strategist_model
313
+ )
314
+
315
+ with gr.Blocks(title="Project: Conductor V4.0 Client") as interface:
316
+ gr.Markdown("# Project: Conductor V4.0 (Client Mode)")
317
+ gr.Markdown(f"### The Command Center: Connected to MCP Server at `{MCP_SERVER_URL}`")
318
+
319
+ # PRIMARY INTERFACE ROW - Three Columns (Directive: Command Console Layout)
320
+ with gr.Row():
321
+ # COLUMN 1 - CODER (Left Flank)
322
+ with gr.Column():
323
+ gr.Markdown("### Coder Agent")
324
+ coder_provider = gr.Dropdown(
325
+ choices=list(PROVIDER_MODEL_MAPPING.keys()),
326
+ value=initial_coder_provider,
327
+ label="Provider",
328
+ interactive=True
329
+ )
330
+ coder_model = gr.Dropdown(
331
+ choices=CLIENT_CONFIG['coder_by_provider'].get(initial_coder_provider, []),
332
+ value=coder_default if coder_default else None,
333
+ label="Model",
334
+ interactive=True
335
+ )
336
+ blueprint_input = gr.Textbox(
337
+ label="Architect's Blueprint",
338
+ placeholder="Enter detailed technical specifications...",
339
+ lines=8
340
+ )
341
+
342
+ # COLUMN 2 - MEDIA BOX (Center Stage)
343
+ with gr.Column():
344
+ default_image = gr.Image(
345
+ value="ai_creative_tech .png",
346
+ show_label=False,
347
+ visible=True,
348
+ interactive=False,
349
+ type="filepath",
350
+ height=450
351
+ )
352
+ working_video = gr.Video(
353
+ value="ai_ct_working.mp4",
354
+ show_label=False,
355
+ visible=False,
356
+ autoplay=True,
357
+ loop=True,
358
+ height=450
359
+ )
360
+ celebrate_video = gr.Video(
361
+ value="ai_creative_tech_celebrate.mp4",
362
+ show_label=False,
363
+ visible=False,
364
+ autoplay=True,
365
+ loop=False,
366
+ height=450
367
+ )
368
+
369
+ # COLUMN 3 - STRATEGIST (Right Flank)
370
+ with gr.Column():
371
+ gr.Markdown("### Strategist Agent")
372
+ strategist_provider = gr.Dropdown(
373
+ choices=list(PROVIDER_MODEL_MAPPING.keys()),
374
+ value=initial_strategist_provider,
375
+ label="Provider",
376
+ interactive=True
377
+ )
378
+ strategist_model = gr.Dropdown(
379
+ choices=CLIENT_CONFIG['strategist_by_provider'].get(initial_strategist_provider, []),
380
+ value=strategist_default if strategist_default else None,
381
+ label="Model",
382
+ interactive=True
383
+ )
384
+ intent_input = gr.Textbox(
385
+ label="Architect's Intent",
386
+ placeholder="Enter project goals and context...",
387
+ lines=8
388
+ )
389
+
390
+ # ACTION ROW - Buttons
391
+ with gr.Row():
392
+ demo_btn = gr.Button("LOAD DEMO MODE", variant="secondary")
393
+ execute_btn = gr.Button("EXECUTE VIA MCP", variant="primary")
394
+ reset_btn = gr.Button("RESET MISSION", variant="secondary")
395
+
396
+ # LOG ROWS
397
+ with gr.Row():
398
+ system_terminal = gr.Textbox(label="SYSTEM LOG", interactive=False, lines=15, max_lines=15)
399
+ coder_log = gr.Textbox(label="CODER LOG", interactive=False, lines=15, max_lines=15)
400
+ strategist_log = gr.Textbox(label="STRATEGIST LOG", interactive=False, lines=15, max_lines=15)
401
+
402
+ with gr.Row():
403
+ master_log_output = gr.HTML(
404
+ value='<div style="font-family: monospace; color: #6b7280;">No mission log data available.</div>',
405
+ label="MASTER LOG WINDOW"
406
+ )
407
+
408
+ # EVENT HANDLERS
409
+
410
+ # Cascading model selector logic
411
+ coder_provider.change(
412
+ fn=update_coder_models,
413
+ inputs=[coder_provider],
414
+ outputs=[coder_model]
415
+ )
416
+
417
+ strategist_provider.change(
418
+ fn=update_strategist_models,
419
+ inputs=[strategist_provider],
420
+ outputs=[strategist_model]
421
+ )
422
+
423
+ # Demo button
424
+ demo_btn.click(
425
+ fn=load_demo_data,
426
+ inputs=None,
427
+ outputs=[blueprint_input, intent_input]
428
+ )
429
+
430
+ # Execute button
431
+ execute_btn.click(
432
+ fn=start_execution_sequence,
433
+ inputs=None,
434
+ outputs=[default_image, working_video, celebrate_video]
435
+ ).then(
436
+ fn=execute_remote_workflow,
437
+ inputs=[blueprint_input, intent_input, coder_model, strategist_model],
438
+ outputs=[system_terminal, coder_log, strategist_log, master_log_output]
439
+ ).then(
440
+ fn=end_execution_sequence,
441
+ inputs=[system_terminal, coder_log, strategist_log, master_log_output],
442
+ outputs=[system_terminal, coder_log, strategist_log, master_log_output, working_video, celebrate_video]
443
+ )
444
+
445
+ # Celebrate video auto-reset
446
+ celebrate_video.stop(
447
+ fn=reset_to_default,
448
+ inputs=None,
449
+ outputs=[default_image, working_video, celebrate_video]
450
+ )
451
+
452
+ # RESET MISSION button (Directive 2.1)
453
+ reset_btn.click(
454
+ fn=reset_mission,
455
+ inputs=[],
456
+ outputs=[
457
+ blueprint_input,
458
+ intent_input,
459
+ system_terminal,
460
+ coder_log,
461
+ strategist_log,
462
+ master_log_output,
463
+ coder_provider,
464
+ coder_model,
465
+ strategist_provider,
466
+ strategist_model
467
+ ]
468
+ )
469
+
470
+ gr.Markdown("---")
471
+ gr.Markdown("**Note:** This client requires the Conductor MCP Server V4.0 with streaming support. Logs now update in real-time with color-coded visual differentiation.")
472
+
473
+ return interface
474
+
475
+ if __name__ == "__main__":
476
+ app = create_interface()
477
+ app.queue()
478
+ app.launch()