Spaces:
Sleeping
Sleeping
Upload app.py
Browse files
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()
|