Deminiko commited on
Commit
440f6ef
·
1 Parent(s): cc59668
Files changed (5) hide show
  1. app.py +19 -13
  2. client/mcp_client.py +176 -60
  3. tasks-project-state.json +34 -5
  4. ui/mcp_health.py +6 -2
  5. ui/quick_build.py +4 -2
app.py CHANGED
@@ -69,15 +69,16 @@ MCP_ENDPOINTS = [
69
  # =============================================================================
70
 
71
  def check_mcp_health() -> str:
72
- """Check overall MCP server health."""
73
  try:
74
- response = requests.get(f"{MCP_SERVER_URL}/", timeout=10)
 
75
  if response.status_code == 200:
76
  return f"🟢 **Connected** to MCP Server"
77
  else:
78
  return f"🟡 **Partial** - Status {response.status_code}"
79
  except requests.exceptions.Timeout:
80
- return "🟠 **Timeout** - Server slow to respond"
81
  except requests.exceptions.ConnectionError:
82
  return "🔴 **Disconnected** - Cannot reach server"
83
  except Exception as e:
@@ -85,13 +86,14 @@ def check_mcp_health() -> str:
85
 
86
 
87
  def check_endpoint_health(endpoint_name: str) -> Dict:
88
- """Check health of a specific MCP endpoint."""
89
  start = time.perf_counter()
90
  try:
91
  url = f"{MCP_SERVER_URL}/gradio_api/call/ui_{endpoint_name}"
92
- response = requests.post(url, json={"data": []}, timeout=15)
 
93
  elapsed = (time.perf_counter() - start) * 1000
94
-
95
  if response.status_code == 200:
96
  return {"status": "🟢", "latency_ms": round(elapsed, 1), "error": None}
97
  elif response.status_code == 404:
@@ -99,12 +101,11 @@ def check_endpoint_health(endpoint_name: str) -> Dict:
99
  else:
100
  return {"status": "🟠", "latency_ms": round(elapsed, 1), "error": f"HTTP {response.status_code}"}
101
  except requests.exceptions.Timeout:
102
- return {"status": "🟠", "latency_ms": 15000, "error": "Timeout"}
 
103
  except Exception as e:
104
  elapsed = (time.perf_counter() - start) * 1000
105
  return {"status": "🔴", "latency_ms": round(elapsed, 1), "error": str(e)[:50]}
106
-
107
-
108
  def get_all_endpoints_health() -> str:
109
  """Get health status of all MCP endpoints as formatted markdown."""
110
  output_lines = [
@@ -200,21 +201,26 @@ I can help you with quantum circuits! Try asking me to:
200
  # =============================================================================
201
 
202
  def quick_build_circuit(template: str, num_qubits: int) -> str:
203
- """Generate a circuit from template using MCP client."""
204
  try:
205
  from client.mcp_client import get_client
206
  mcp_client = get_client(MCP_SERVER_URL)
207
- result = mcp_client.create_circuit_from_template(template, int(num_qubits))
208
 
 
 
 
 
 
 
 
209
  if result.success and result.data:
210
  if isinstance(result.data, dict) and 'qasm' in result.data:
211
  return result.data['qasm']
212
  return str(result.data)
213
  return f"# Error: {result.error or 'Unknown error'}"
214
  except Exception as e:
 
215
  return f"# Error: {str(e)}"
216
-
217
-
218
  # =============================================================================
219
  # GRADIO INTERFACE
220
  # =============================================================================
 
69
  # =============================================================================
70
 
71
  def check_mcp_health() -> str:
72
+ """Check overall MCP server health with extended timeout for cold starts."""
73
  try:
74
+ # Extended timeout to handle HuggingFace Space cold starts (up to 60s)
75
+ response = requests.get(f"{MCP_SERVER_URL}/", timeout=60)
76
  if response.status_code == 200:
77
  return f"🟢 **Connected** to MCP Server"
78
  else:
79
  return f"🟡 **Partial** - Status {response.status_code}"
80
  except requests.exceptions.Timeout:
81
+ return "🟠 **Timeout** - Server may be starting up (cold start can take 30-60s)"
82
  except requests.exceptions.ConnectionError:
83
  return "🔴 **Disconnected** - Cannot reach server"
84
  except Exception as e:
 
86
 
87
 
88
  def check_endpoint_health(endpoint_name: str) -> Dict:
89
+ """Check health of a specific MCP endpoint with extended timeout."""
90
  start = time.perf_counter()
91
  try:
92
  url = f"{MCP_SERVER_URL}/gradio_api/call/ui_{endpoint_name}"
93
+ # Extended timeout for HuggingFace Space cold starts
94
+ response = requests.post(url, json={"data": []}, timeout=90)
95
  elapsed = (time.perf_counter() - start) * 1000
96
+
97
  if response.status_code == 200:
98
  return {"status": "🟢", "latency_ms": round(elapsed, 1), "error": None}
99
  elif response.status_code == 404:
 
101
  else:
102
  return {"status": "🟠", "latency_ms": round(elapsed, 1), "error": f"HTTP {response.status_code}"}
103
  except requests.exceptions.Timeout:
104
+ elapsed = (time.perf_counter() - start) * 1000
105
+ return {"status": "🟠", "latency_ms": round(elapsed, 1), "error": "Timeout (server may be cold)"}
106
  except Exception as e:
107
  elapsed = (time.perf_counter() - start) * 1000
108
  return {"status": "🔴", "latency_ms": round(elapsed, 1), "error": str(e)[:50]}
 
 
109
  def get_all_endpoints_health() -> str:
110
  """Get health status of all MCP endpoints as formatted markdown."""
111
  output_lines = [
 
201
  # =============================================================================
202
 
203
  def quick_build_circuit(template: str, num_qubits: int) -> str:
204
+ """Generate a circuit from template using MCP client with retry and fallback."""
205
  try:
206
  from client.mcp_client import get_client
207
  mcp_client = get_client(MCP_SERVER_URL)
 
208
 
209
+ # Try to warm up server first (handles HF Space cold start)
210
+ if not mcp_client._server_warmed:
211
+ logger.info("Warming up MCP server...")
212
+ mcp_client.warm_up_server()
213
+
214
+ result = mcp_client.create_circuit_from_template(template, int(num_qubits))
215
+
216
  if result.success and result.data:
217
  if isinstance(result.data, dict) and 'qasm' in result.data:
218
  return result.data['qasm']
219
  return str(result.data)
220
  return f"# Error: {result.error or 'Unknown error'}"
221
  except Exception as e:
222
+ logger.error(f"Quick build error: {e}")
223
  return f"# Error: {str(e)}"
 
 
224
  # =============================================================================
225
  # GRADIO INTERFACE
226
  # =============================================================================
client/mcp_client.py CHANGED
@@ -1,6 +1,7 @@
1
- # Path: QAgents-workflos/client/mcp_client.py
2
- # Relations: Uses QuantumArchitect-MCP Gradio server
3
  # Description: MCP client with fallback local implementations for missing endpoints
 
4
  """
5
  MCP Client: Connection to QuantumArchitect-MCP endpoints.
6
  Provides both synchronous and async interfaces.
@@ -12,6 +13,11 @@ Available Gradio endpoints (as of latest scan):
12
  - ui_score_circuit: Score circuit complexity/fitness
13
 
14
  Missing endpoints use local fallback implementations.
 
 
 
 
 
15
  """
16
 
17
  import requests
@@ -24,9 +30,19 @@ import re
24
  import time
25
  import random
26
  import math
 
27
 
28
  logger = logging.getLogger(__name__)
29
 
 
 
 
 
 
 
 
 
 
30
 
31
  @dataclass
32
  class MCPResponse:
@@ -227,70 +243,139 @@ class MCPClient:
227
  """
228
  Client for QuantumArchitect-MCP server.
229
  Wraps MCP endpoints with fallback to local implementations.
230
-
231
  Primary endpoints (from Gradio):
232
  - ui_create_circuit
233
  - ui_validate_circuit
234
  - ui_simulate_circuit
235
  - ui_score_circuit
236
-
237
  Missing endpoints use QASMLocalAnalyzer for fallback.
 
 
 
 
 
 
238
  """
239
 
240
- def __init__(self, base_url: str = "http://127.0.0.1:7861"):
 
 
241
  self.base_url = base_url.rstrip("/")
242
  self.session = requests.Session()
243
  self._connected = False
244
  self._analyzer = QASMLocalAnalyzer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
  def _call(self, endpoint: str, **kwargs) -> MCPResponse:
247
- """Internal method to call MCP endpoints."""
248
  start = time.perf_counter()
249
-
250
- try:
251
- url = f"{self.base_url}/gradio_api/call/{endpoint}"
252
- payload = {"data": list(kwargs.values()) if kwargs else []}
 
 
 
 
 
 
 
 
 
 
253
 
254
- response = self.session.post(url, json=payload, timeout=30)
255
- response.raise_for_status()
256
-
257
- result = response.json()
258
- event_id = result.get("event_id")
259
-
260
- if event_id:
261
- result_url = f"{self.base_url}/gradio_api/call/{endpoint}/{event_id}"
262
- result_response = self.session.get(result_url, timeout=30)
263
-
264
- lines = result_response.text.strip().split("\n")
265
- for line in lines:
266
- if line.startswith("data:"):
267
- data = json.loads(line[5:].strip())
268
- elapsed = (time.perf_counter() - start) * 1000
269
- return MCPResponse(
270
- success=True,
271
- data=data[0] if isinstance(data, list) and len(data) == 1 else data,
272
- endpoint=endpoint,
273
- execution_time_ms=elapsed
274
- )
275
-
276
- elapsed = (time.perf_counter() - start) * 1000
277
- return MCPResponse(
278
- success=True,
279
- data=result,
280
- endpoint=endpoint,
281
- execution_time_ms=elapsed
282
- )
283
 
284
- except Exception as e:
285
- elapsed = (time.perf_counter() - start) * 1000
286
- logger.warning(f"MCP call failed: {endpoint} - {e}")
287
- return MCPResponse(
288
- success=False,
289
- data=None,
290
- endpoint=endpoint,
291
- error=str(e),
292
- execution_time_ms=elapsed
293
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
294
 
295
  def _fallback_response(self, endpoint: str, data: Any, start_time: float) -> MCPResponse:
296
  """Create a fallback response using local implementation."""
@@ -306,10 +391,15 @@ class MCPClient:
306
  def health_check(self) -> bool:
307
  """Check if MCP server is reachable."""
308
  try:
309
- response = self.session.get(f"{self.base_url}/", timeout=5)
310
  self._connected = response.status_code == 200
311
  return self._connected
312
- except:
 
 
 
 
 
313
  self._connected = False
314
  return False
315
 
@@ -399,8 +489,27 @@ class MCPClient:
399
 
400
  # ===== Validation Endpoints =====
401
 
402
- def validate_syntax(self, qasm_code: str) -> MCPResponse:
403
- """Validate QASM syntax. Maps to ui_validate_circuit."""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
404
  return self._call("ui_validate_circuit", qasm=qasm_code, hardware="")
405
 
406
  def check_connectivity(self, qasm_code: str, hardware: str = "ibm_brisbane") -> MCPResponse:
@@ -687,15 +796,22 @@ def get_client(base_url: Optional[str] = None) -> MCPClient:
687
 
688
  Args:
689
  base_url: Optional URL override. If None, checks MCP_SERVER_URL env var,
690
- then defaults to the HuggingFace Space URL
 
 
 
691
  """
692
  global _client
693
  if _client is None:
694
  if base_url is None:
695
- import os
696
- base_url = os.environ.get(
697
- "MCP_SERVER_URL",
698
- "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space"
699
- )
700
  _client = MCPClient(base_url)
701
- return _client
 
 
 
 
 
 
 
 
 
1
+ # Path: QAgents-workflows/client/mcp_client.py
2
+ # Relations: Uses QuantumArchitect-MCP Gradio server (HuggingFace Space)
3
  # Description: MCP client with fallback local implementations for missing endpoints
4
+ # Includes retry logic and extended timeouts for HF Space cold starts
5
  """
6
  MCP Client: Connection to QuantumArchitect-MCP endpoints.
7
  Provides both synchronous and async interfaces.
 
13
  - ui_score_circuit: Score circuit complexity/fitness
14
 
15
  Missing endpoints use local fallback implementations.
16
+
17
+ HuggingFace Space Considerations:
18
+ - Spaces go to sleep after inactivity (cold start takes 30-60s)
19
+ - Extended timeouts and retry logic handle this gracefully
20
+ - Local fallback used when MCP server is unreachable
21
  """
22
 
23
  import requests
 
30
  import time
31
  import random
32
  import math
33
+ import os
34
 
35
  logger = logging.getLogger(__name__)
36
 
37
+ # Default MCP Server URL (HuggingFace Space)
38
+ DEFAULT_MCP_URL = "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space"
39
+
40
+ # Timeout settings for HuggingFace Spaces
41
+ INITIAL_TIMEOUT = 90 # First request - allow cold start time
42
+ RESULT_TIMEOUT = 120 # Result retrieval - allow processing time
43
+ HEALTH_TIMEOUT = 30 # Health check timeout
44
+ MAX_RETRIES = 3 # Number of retries for transient failures
45
+
46
 
47
  @dataclass
48
  class MCPResponse:
 
243
  """
244
  Client for QuantumArchitect-MCP server.
245
  Wraps MCP endpoints with fallback to local implementations.
246
+
247
  Primary endpoints (from Gradio):
248
  - ui_create_circuit
249
  - ui_validate_circuit
250
  - ui_simulate_circuit
251
  - ui_score_circuit
252
+
253
  Missing endpoints use QASMLocalAnalyzer for fallback.
254
+
255
+ Features:
256
+ - Extended timeouts for HuggingFace Space cold starts
257
+ - Automatic retry with exponential backoff
258
+ - Server warm-up before first request
259
+ - Graceful fallback to local implementations
260
  """
261
 
262
+ def __init__(self, base_url: str = None):
263
+ if base_url is None:
264
+ base_url = os.environ.get("MCP_SERVER_URL", DEFAULT_MCP_URL)
265
  self.base_url = base_url.rstrip("/")
266
  self.session = requests.Session()
267
  self._connected = False
268
  self._analyzer = QASMLocalAnalyzer()
269
+ self._server_warmed = False
270
+ logger.info(f"MCPClient initialized with base_url: {self.base_url}")
271
+
272
+ def warm_up_server(self) -> bool:
273
+ """
274
+ Wake up HuggingFace Space before making requests.
275
+ Spaces go to sleep after inactivity and need time to start.
276
+
277
+ Returns:
278
+ True if server is warmed up and ready
279
+ """
280
+ if self._server_warmed:
281
+ return True
282
+
283
+ logger.info(f"Warming up MCP server at {self.base_url}...")
284
+
285
+ for attempt in range(MAX_RETRIES):
286
+ try:
287
+ # Simple GET to wake up the server
288
+ response = self.session.get(
289
+ f"{self.base_url}/",
290
+ timeout=INITIAL_TIMEOUT
291
+ )
292
+ if response.status_code == 200:
293
+ self._server_warmed = True
294
+ self._connected = True
295
+ logger.info("MCP server is ready")
296
+ return True
297
+ except requests.exceptions.Timeout:
298
+ logger.warning(f"Warm-up attempt {attempt + 1}/{MAX_RETRIES} timed out, retrying...")
299
+ except requests.exceptions.ConnectionError as e:
300
+ logger.warning(f"Warm-up attempt {attempt + 1}/{MAX_RETRIES} connection error: {e}")
301
+ except Exception as e:
302
+ logger.warning(f"Warm-up attempt {attempt + 1}/{MAX_RETRIES} failed: {e}")
303
+
304
+ if attempt < MAX_RETRIES - 1:
305
+ wait_time = 2 ** attempt # 1s, 2s, 4s
306
+ time.sleep(wait_time)
307
+
308
+ logger.warning("Failed to warm up MCP server, will use local fallback")
309
+ return False
310
 
311
  def _call(self, endpoint: str, **kwargs) -> MCPResponse:
312
+ """Internal method to call MCP endpoints with retry logic."""
313
  start = time.perf_counter()
314
+ last_error = None
315
+
316
+ for attempt in range(MAX_RETRIES):
317
+ try:
318
+ return self._call_once(endpoint, start, **kwargs)
319
+ except requests.exceptions.Timeout as e:
320
+ last_error = f"Timeout after {INITIAL_TIMEOUT}s"
321
+ logger.warning(f"MCP call {endpoint} attempt {attempt + 1}/{MAX_RETRIES} timed out")
322
+ except requests.exceptions.ConnectionError as e:
323
+ last_error = f"Connection error: {e}"
324
+ logger.warning(f"MCP call {endpoint} attempt {attempt + 1}/{MAX_RETRIES} connection error")
325
+ except Exception as e:
326
+ last_error = str(e)
327
+ logger.warning(f"MCP call {endpoint} attempt {attempt + 1}/{MAX_RETRIES} failed: {e}")
328
 
329
+ if attempt < MAX_RETRIES - 1:
330
+ wait_time = 2 ** attempt # Exponential backoff: 1s, 2s, 4s
331
+ time.sleep(wait_time)
332
+
333
+ # All retries failed
334
+ elapsed = (time.perf_counter() - start) * 1000
335
+ logger.error(f"MCP call {endpoint} failed after {MAX_RETRIES} attempts: {last_error}")
336
+ return MCPResponse(
337
+ success=False,
338
+ data=None,
339
+ endpoint=endpoint,
340
+ error=last_error,
341
+ execution_time_ms=elapsed
342
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
343
 
344
+ def _call_once(self, endpoint: str, start: float, **kwargs) -> MCPResponse:
345
+ """Single attempt to call an MCP endpoint."""
346
+ url = f"{self.base_url}/gradio_api/call/{endpoint}"
347
+ payload = {"data": list(kwargs.values()) if kwargs else []}
348
+
349
+ logger.debug(f"Calling MCP endpoint: {url}")
350
+ response = self.session.post(url, json=payload, timeout=INITIAL_TIMEOUT)
351
+ response.raise_for_status()
352
+
353
+ result = response.json()
354
+ event_id = result.get("event_id")
355
+
356
+ if event_id:
357
+ result_url = f"{self.base_url}/gradio_api/call/{endpoint}/{event_id}"
358
+ result_response = self.session.get(result_url, timeout=RESULT_TIMEOUT)
359
+
360
+ lines = result_response.text.strip().split("\n")
361
+ for line in lines:
362
+ if line.startswith("data:"):
363
+ data = json.loads(line[5:].strip())
364
+ elapsed = (time.perf_counter() - start) * 1000
365
+ return MCPResponse(
366
+ success=True,
367
+ data=data[0] if isinstance(data, list) and len(data) == 1 else data,
368
+ endpoint=endpoint,
369
+ execution_time_ms=elapsed
370
+ )
371
+
372
+ elapsed = (time.perf_counter() - start) * 1000
373
+ return MCPResponse(
374
+ success=True,
375
+ data=result,
376
+ endpoint=endpoint,
377
+ execution_time_ms=elapsed
378
+ )
379
 
380
  def _fallback_response(self, endpoint: str, data: Any, start_time: float) -> MCPResponse:
381
  """Create a fallback response using local implementation."""
 
391
  def health_check(self) -> bool:
392
  """Check if MCP server is reachable."""
393
  try:
394
+ response = self.session.get(f"{self.base_url}/", timeout=HEALTH_TIMEOUT)
395
  self._connected = response.status_code == 200
396
  return self._connected
397
+ except requests.exceptions.Timeout:
398
+ logger.warning(f"Health check timed out after {HEALTH_TIMEOUT}s")
399
+ self._connected = False
400
+ return False
401
+ except Exception as e:
402
+ logger.warning(f"Health check failed: {e}")
403
  self._connected = False
404
  return False
405
 
 
489
 
490
  # ===== Validation Endpoints =====
491
 
492
+ def validate_syntax(self, qasm_code: str, use_local_first: bool = True) -> MCPResponse:
493
+ """
494
+ Validate QASM syntax.
495
+
496
+ Args:
497
+ qasm_code: The QASM code to validate
498
+ use_local_first: If True, use fast local validation first
499
+
500
+ Returns:
501
+ Validation result with any syntax errors
502
+ """
503
+ # Try local validation first (fast, no network)
504
+ if use_local_first:
505
+ start = time.perf_counter()
506
+ local_result = self._analyzer.validate_syntax(qasm_code)
507
+ if local_result['valid']:
508
+ return self._fallback_response("validate_syntax", local_result, start)
509
+ # If local validation found errors, still return them quickly
510
+ return self._fallback_response("validate_syntax", local_result, start)
511
+
512
+ # Use MCP server for full validation
513
  return self._call("ui_validate_circuit", qasm=qasm_code, hardware="")
514
 
515
  def check_connectivity(self, qasm_code: str, hardware: str = "ibm_brisbane") -> MCPResponse:
 
796
 
797
  Args:
798
  base_url: Optional URL override. If None, checks MCP_SERVER_URL env var,
799
+ then defaults to the HuggingFace Space URL
800
+
801
+ Returns:
802
+ MCPClient instance connected to the MCP server
803
  """
804
  global _client
805
  if _client is None:
806
  if base_url is None:
807
+ base_url = os.environ.get("MCP_SERVER_URL", DEFAULT_MCP_URL)
 
 
 
 
808
  _client = MCPClient(base_url)
809
+ logger.info(f"Created MCP client for: {base_url}")
810
+ return _client
811
+
812
+
813
+ def reset_client():
814
+ """Reset the singleton client (useful for testing or reconnection)."""
815
+ global _client
816
+ _client = None
817
+ logger.info("MCP client reset")
tasks-project-state.json CHANGED
@@ -1,16 +1,44 @@
1
  {
2
  "project": "QAgents-Workflows",
3
- "version": "0.9.0",
4
  "description": "Multi-agent quantum circuit optimization system with chat UI and MCP integration",
5
  "last_updated": "2025-11-30",
6
- "status": "CHAT_UI_AND_MCP_HEALTH_MONITOR_ADDED",
7
- "notes": "Added Gradio chat interface for agent interaction. Added MCP endpoints health monitor tab. Updated MCP client to connect to HuggingFace Space. Removed unsupported Blocks theme argument to restore runtime compatibility on Gradio 6.",
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  "huggingface_deployment": {
10
  "qagents_space": "https://huggingface.co/spaces/NLarchive/Qagents-workflows",
11
  "mcp_server_space": "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space",
 
12
  "gradio_version": "6.0.0",
13
- "mcp_enabled": true
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  },
15
 
16
  "app_features": {
@@ -29,7 +57,8 @@
29
  "Check all endpoints health",
30
  "Individual endpoint testing",
31
  "Latency monitoring",
32
- "Status indicators (🟢🟡🟠🔴)"
 
33
  ]
34
  },
35
  "quick_build_tab": {
 
1
  {
2
  "project": "QAgents-Workflows",
3
+ "version": "0.9.1",
4
  "description": "Multi-agent quantum circuit optimization system with chat UI and MCP integration",
5
  "last_updated": "2025-11-30",
6
+ "status": "MCP_CLIENT_TIMEOUT_FIX_APPLIED",
7
+ "notes": "Fixed MCP client timeout issues for HuggingFace Space cold starts. Increased timeouts (90s initial, 120s result), added retry logic with exponential backoff, added server warm-up function. Chat UI and MCP health monitor working. Local validation fallback for fast syntax checks.",
8
+
9
+ "recent_changes": {
10
+ "2025-11-30": [
11
+ "Fixed timeout errors in mcp_client.py for HuggingFace Space cold starts",
12
+ "Increased INITIAL_TIMEOUT to 90s, RESULT_TIMEOUT to 120s",
13
+ "Added MAX_RETRIES=3 with exponential backoff",
14
+ "Added warm_up_server() method to MCPClient",
15
+ "Updated validate_syntax() to use local fallback first (faster)",
16
+ "Updated app.py health check functions with extended timeouts",
17
+ "Improved error messages to indicate cold start situations"
18
+ ]
19
+ },
20
 
21
  "huggingface_deployment": {
22
  "qagents_space": "https://huggingface.co/spaces/NLarchive/Qagents-workflows",
23
  "mcp_server_space": "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space",
24
+ "mcp_sse_endpoint": "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space/gradio_api/mcp/sse",
25
  "gradio_version": "6.0.0",
26
+ "mcp_enabled": true,
27
+ "cold_start_note": "HF Spaces go to sleep after inactivity. First request may take 30-60s."
28
+ },
29
+
30
+ "mcp_client_config": {
31
+ "default_url": "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space",
32
+ "initial_timeout_s": 90,
33
+ "result_timeout_s": 120,
34
+ "health_timeout_s": 30,
35
+ "max_retries": 3,
36
+ "features": [
37
+ "Extended timeouts for cold starts",
38
+ "Retry with exponential backoff",
39
+ "Server warm-up before first request",
40
+ "Local fallback for validation"
41
+ ]
42
  },
43
 
44
  "app_features": {
 
57
  "Check all endpoints health",
58
  "Individual endpoint testing",
59
  "Latency monitoring",
60
+ "Status indicators (🟢🟡🟠🔴)",
61
+ "Extended timeouts for cold starts"
62
  ]
63
  },
64
  "quick_build_tab": {
ui/mcp_health.py CHANGED
@@ -1,14 +1,18 @@
1
  # Path: QAgents-workflows/ui/mcp_health.py
2
  # Relations: Uses client/mcp_client.py for health checks
3
  # Used by __init__.py, app.py, chat_components.py
4
- # Description: MCP health monitoring UI components
 
5
  """
6
  MCP Health Components: Monitor QuantumArchitect-MCP endpoint availability.
7
 
8
  Provides:
9
- - Server health check
10
  - Individual endpoint health checks
11
  - Health status table display
 
 
 
12
  """
13
 
14
  import os
 
1
  # Path: QAgents-workflows/ui/mcp_health.py
2
  # Relations: Uses client/mcp_client.py for health checks
3
  # Used by __init__.py, app.py, chat_components.py
4
+ # Description: MCP health monitoring UI components with extended timeouts
5
+ # for HuggingFace Space cold starts
6
  """
7
  MCP Health Components: Monitor QuantumArchitect-MCP endpoint availability.
8
 
9
  Provides:
10
+ - Server health check with extended timeout for cold starts
11
  - Individual endpoint health checks
12
  - Health status table display
13
+
14
+ Note: HuggingFace Spaces go to sleep after inactivity. First request may
15
+ take 30-60 seconds while the server wakes up (cold start).
16
  """
17
 
18
  import os
ui/quick_build.py CHANGED
@@ -1,14 +1,16 @@
1
  # Path: QAgents-workflows/ui/quick_build.py
2
  # Relations: Uses client/mcp_client.py for circuit generation
3
  # Used by __init__.py, app.py
4
- # Description: Quick circuit builder UI components
5
  """
6
  Quick Build Components: Fast circuit generation from templates.
7
 
8
  Provides:
9
  - Template selection dropdown
10
  - Qubit count slider
11
- - Circuit generation via MCP client
 
 
12
  """
13
 
14
  import os
 
1
  # Path: QAgents-workflows/ui/quick_build.py
2
  # Relations: Uses client/mcp_client.py for circuit generation
3
  # Used by __init__.py, app.py
4
+ # Description: Quick circuit builder UI components with improved error handling
5
  """
6
  Quick Build Components: Fast circuit generation from templates.
7
 
8
  Provides:
9
  - Template selection dropdown
10
  - Qubit count slider
11
+ - Circuit generation via MCP client with retry and fallback
12
+
13
+ Note: First request may take longer if MCP server needs to wake up (cold start).
14
  """
15
 
16
  import os