Deminiko commited on
Commit
4cfa289
·
1 Parent(s): 227f42e

feat: Add modular UI components for Gradio 6.0 compatibility

Browse files

- Create ui/ module with reusable components
- ui/styles.py: Custom CSS for consistent styling
- ui/chat_components.py: Chat tab with NAKED mode
- ui/mcp_health.py: MCP endpoint health monitoring
- ui/quick_build.py: Template circuit generation
- Update app.py to import UI styles with fallback

Files changed (6) hide show
  1. app.py +15 -3
  2. ui/__init__.py +49 -0
  3. ui/chat_components.py +182 -0
  4. ui/mcp_health.py +183 -0
  5. ui/quick_build.py +112 -0
  6. ui/styles.py +163 -0
app.py CHANGED
@@ -1,9 +1,11 @@
1
  """
2
  QAgents-Workflows: Hugging Face Space Entry Point
3
  Path: QAgents-workflows/app.py
4
- Related: client/mcp_client.py (MCP connection), orchestrators/ (agent orchestration)
 
 
5
 
6
- Provides a Gradio interface with:
7
  - Chat UI for interacting with quantum circuit agents (NAKED mode)
8
  - MCP Endpoints health monitoring tab
9
  - Circuit generation and validation tools
@@ -18,6 +20,16 @@ import requests
18
  import time
19
  from typing import Optional, List, Dict, Any
20
 
 
 
 
 
 
 
 
 
 
 
21
  # Configure logging
22
  logging.basicConfig(level=logging.INFO)
23
  logger = logging.getLogger(__name__)
@@ -214,7 +226,7 @@ def quick_build_circuit(template: str, num_qubits: int) -> str:
214
  # GRADIO INTERFACE
215
  # =============================================================================
216
 
217
- with gr.Blocks(title="QAgents - Quantum Circuit Assistant") as demo:
218
 
219
  # Header
220
  gr.Markdown("""
 
1
  """
2
  QAgents-Workflows: Hugging Face Space Entry Point
3
  Path: QAgents-workflows/app.py
4
+ Related: ui/ module for Gradio components
5
+ client/mcp_client.py (MCP connection)
6
+ orchestrators/ (agent orchestration)
7
 
8
+ Provides a Gradio 6.0 compatible interface with:
9
  - Chat UI for interacting with quantum circuit agents (NAKED mode)
10
  - MCP Endpoints health monitoring tab
11
  - Circuit generation and validation tools
 
20
  import time
21
  from typing import Optional, List, Dict, Any
22
 
23
+ # =============================================================================
24
+ # TRY TO IMPORT UI MODULE (provides modular components)
25
+ # =============================================================================
26
+ try:
27
+ from ui.styles import CUSTOM_CSS
28
+ UI_STYLES_AVAILABLE = True
29
+ except ImportError:
30
+ CUSTOM_CSS = ""
31
+ UI_STYLES_AVAILABLE = False
32
+
33
  # Configure logging
34
  logging.basicConfig(level=logging.INFO)
35
  logger = logging.getLogger(__name__)
 
226
  # GRADIO INTERFACE
227
  # =============================================================================
228
 
229
+ with gr.Blocks(title="QAgents - Quantum Circuit Assistant", css=CUSTOM_CSS if UI_STYLES_AVAILABLE else None) as demo:
230
 
231
  # Header
232
  gr.Markdown("""
ui/__init__.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Path: QAgents-workflows/ui/__init__.py
2
+ # Relations: Used by app.py, imports from chat_components.py, mcp_health.py, quick_build.py
3
+ # Description: UI module initialization - exports all Gradio components for the app
4
+ """
5
+ UI Module: Gradio 6.0 compatible UI components for QAgents-Workflows.
6
+
7
+ This module provides modular UI components that can be assembled
8
+ into the main Gradio app. Each component is designed to work
9
+ with the agent orchestration system.
10
+ """
11
+
12
+ from .chat_components import (
13
+ create_chat_tab,
14
+ chat_response,
15
+ generate_circuit_with_naked,
16
+ )
17
+
18
+ from .mcp_health import (
19
+ create_mcp_health_tab,
20
+ check_mcp_health,
21
+ check_endpoint_health,
22
+ get_all_endpoints_health,
23
+ MCP_ENDPOINTS,
24
+ )
25
+
26
+ from .quick_build import (
27
+ create_quick_build_tab,
28
+ quick_build_circuit,
29
+ )
30
+
31
+ from .styles import CUSTOM_CSS
32
+
33
+ __all__ = [
34
+ # Chat
35
+ "create_chat_tab",
36
+ "chat_response",
37
+ "generate_circuit_with_naked",
38
+ # MCP Health
39
+ "create_mcp_health_tab",
40
+ "check_mcp_health",
41
+ "check_endpoint_health",
42
+ "get_all_endpoints_health",
43
+ "MCP_ENDPOINTS",
44
+ # Quick Build
45
+ "create_quick_build_tab",
46
+ "quick_build_circuit",
47
+ # Styles
48
+ "CUSTOM_CSS",
49
+ ]
ui/chat_components.py ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Path: QAgents-workflows/ui/chat_components.py
2
+ # Relations: Uses orchestrators/orchestrator.py (NakedOrchestrator)
3
+ # Used by __init__.py, app.py
4
+ # Description: Chat UI components for interacting with quantum circuit agents
5
+ """
6
+ Chat Components: Gradio 6.0 compatible chat interface for QAgents.
7
+
8
+ Provides:
9
+ - Chat tab creation
10
+ - Message handling with NAKED mode orchestrator
11
+ - Help and status commands
12
+ """
13
+
14
+ import gradio as gr
15
+ import logging
16
+ from typing import List, Dict, Any, Tuple
17
+
18
+ logger = logging.getLogger(__name__)
19
+
20
+
21
+ def generate_circuit_with_naked(prompt: str) -> str:
22
+ """
23
+ Generate a quantum circuit using NAKED mode (direct LLM call).
24
+ This is the simplest and fastest mode.
25
+
26
+ Args:
27
+ prompt: User's natural language request
28
+
29
+ Returns:
30
+ Formatted response with circuit or error message
31
+ """
32
+ try:
33
+ # Lazy import to avoid startup issues
34
+ from orchestrators import create_orchestrator
35
+
36
+ orch = create_orchestrator("naked")
37
+ result = orch.run(prompt)
38
+
39
+ if result.success:
40
+ output = f"✅ **Success** ({result.execution_time_ms:.0f}ms)\n\n"
41
+ if result.final_output:
42
+ if 'OPENQASM' in str(result.final_output) or 'qreg' in str(result.final_output):
43
+ output += f"```qasm\n{result.final_output}\n```"
44
+ else:
45
+ output += str(result.final_output)
46
+ return output
47
+ else:
48
+ error_msg = "\n".join(result.errors) if result.errors else "Unknown error"
49
+ return f"❌ **Failed** ({result.execution_time_ms:.0f}ms)\n\n{error_msg}"
50
+ except Exception as e:
51
+ logger.error(f"NAKED mode error: {e}")
52
+ return f"❌ **Error**: {str(e)}"
53
+
54
+
55
+ def get_help_text() -> str:
56
+ """Return help text for the chat interface."""
57
+ return """## 🤖 QAgents Help
58
+
59
+ I can help you with quantum circuits! Try asking me to:
60
+
61
+ **Create Circuits:**
62
+ - "Create a Bell state"
63
+ - "Generate a 3-qubit GHZ state"
64
+ - "Make a QFT circuit for 4 qubits"
65
+ - "Build a simple superposition"
66
+
67
+ **Examples:**
68
+ - "Create a circuit that puts 2 qubits in superposition"
69
+ - "Generate a CNOT gate between qubit 0 and 1"
70
+ - "Build a quantum teleportation circuit"
71
+
72
+ 💡 **Tip:** Be specific about the number of qubits and desired operations!
73
+
74
+ **Commands:**
75
+ - `help` - Show this help message
76
+ - `status` - Check system status"""
77
+
78
+
79
+ def get_status_text(mcp_server_url: str) -> str:
80
+ """Return status text for the chat interface."""
81
+ try:
82
+ from ui.mcp_health import check_mcp_health
83
+ health = check_mcp_health()
84
+ return f"## 📊 System Status\n\n{health}\n\n**MCP Server:** `{mcp_server_url}`"
85
+ except Exception as e:
86
+ return f"## 📊 System Status\n\n🔴 **Error checking status**: {str(e)}"
87
+
88
+
89
+ def chat_response(message: str, history: List, mcp_server_url: str = "") -> str:
90
+ """
91
+ Handle chat messages and generate responses.
92
+ Uses NAKED mode for circuit generation.
93
+
94
+ Args:
95
+ message: User's message
96
+ history: Chat history
97
+ mcp_server_url: URL of the MCP server
98
+
99
+ Returns:
100
+ Bot's response message
101
+ """
102
+ if not message.strip():
103
+ return ""
104
+
105
+ message_lower = message.lower().strip()
106
+
107
+ # Help command
108
+ if message_lower in ['help', '/help', '?']:
109
+ return get_help_text()
110
+
111
+ # Status command
112
+ if message_lower in ['status', '/status']:
113
+ return get_status_text(mcp_server_url)
114
+
115
+ # Generate circuit
116
+ logger.info(f"Generating circuit for: {message}")
117
+ return generate_circuit_with_naked(message)
118
+
119
+
120
+ def create_chat_tab(mcp_server_url: str = "") -> Tuple[gr.Chatbot, gr.Textbox, gr.Button]:
121
+ """
122
+ Create the chat tab components for Gradio 6.0.
123
+
124
+ Args:
125
+ mcp_server_url: URL of the MCP server for status checks
126
+
127
+ Returns:
128
+ Tuple of (chatbot, textbox, send_button) components
129
+ """
130
+ gr.Markdown("### Chat with Quantum Circuit Agent")
131
+ gr.Markdown("Ask me to create quantum circuits! Try: *'Create a Bell state'* or *'Generate a 3-qubit GHZ state'*")
132
+
133
+ chatbot = gr.Chatbot(
134
+ value=[],
135
+ height=400,
136
+ label="Quantum Circuit Agent"
137
+ )
138
+
139
+ with gr.Row():
140
+ msg_input = gr.Textbox(
141
+ placeholder="Ask me to create a quantum circuit...",
142
+ label="Your Message",
143
+ scale=4,
144
+ lines=1
145
+ )
146
+ send_btn = gr.Button("Send 🚀", variant="primary", scale=1)
147
+
148
+ with gr.Row():
149
+ clear_btn = gr.Button("🗑️ Clear", size="sm")
150
+ help_btn = gr.Button("❓ Help", size="sm")
151
+ status_btn = gr.Button("📊 Status", size="sm")
152
+
153
+ # Chat handlers
154
+ def respond(message: str, chat_history: List):
155
+ if not message.strip():
156
+ return "", chat_history
157
+
158
+ bot_response = chat_response(message, chat_history, mcp_server_url)
159
+ chat_history.append({"role": "user", "content": message})
160
+ chat_history.append({"role": "assistant", "content": bot_response})
161
+ return "", chat_history
162
+
163
+ def show_help(chat_history: List):
164
+ help_text = get_help_text()
165
+ chat_history.append({"role": "user", "content": "help"})
166
+ chat_history.append({"role": "assistant", "content": help_text})
167
+ return chat_history
168
+
169
+ def show_status(chat_history: List):
170
+ status_text = get_status_text(mcp_server_url)
171
+ chat_history.append({"role": "user", "content": "status"})
172
+ chat_history.append({"role": "assistant", "content": status_text})
173
+ return chat_history
174
+
175
+ # Wire up events
176
+ send_btn.click(respond, [msg_input, chatbot], [msg_input, chatbot])
177
+ msg_input.submit(respond, [msg_input, chatbot], [msg_input, chatbot])
178
+ clear_btn.click(lambda: [], outputs=[chatbot])
179
+ help_btn.click(show_help, [chatbot], [chatbot])
180
+ status_btn.click(show_status, [chatbot], [chatbot])
181
+
182
+ return chatbot, msg_input, send_btn
ui/mcp_health.py ADDED
@@ -0,0 +1,183 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
15
+ import gradio as gr
16
+ import requests
17
+ import time
18
+ import logging
19
+ from typing import Dict, List
20
+
21
+ logger = logging.getLogger(__name__)
22
+
23
+ # Default MCP Server URL
24
+ MCP_SERVER_URL = os.environ.get(
25
+ "MCP_SERVER_URL",
26
+ "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space"
27
+ )
28
+
29
+ # MCP Endpoints definitions
30
+ MCP_ENDPOINTS = [
31
+ {"name": "create_circuit", "category": "Creation", "description": "Create circuit from template"},
32
+ {"name": "parse_qasm", "category": "Creation", "description": "Parse OpenQASM code"},
33
+ {"name": "build_circuit", "category": "Creation", "description": "Build custom circuit from gates"},
34
+ {"name": "validate_circuit", "category": "Validation", "description": "Full circuit validation"},
35
+ {"name": "check_hardware", "category": "Validation", "description": "Hardware compatibility check"},
36
+ {"name": "simulate", "category": "Simulation", "description": "Simulate with measurements"},
37
+ {"name": "get_statevector", "category": "Simulation", "description": "Extract statevector"},
38
+ {"name": "estimate_fidelity", "category": "Simulation", "description": "Hardware fidelity estimation"},
39
+ {"name": "score_circuit", "category": "Scoring", "description": "Circuit scoring metrics"},
40
+ {"name": "compare_circuits", "category": "Scoring", "description": "Compare multiple circuits"},
41
+ {"name": "get_gate_info", "category": "Documentation", "description": "Gate documentation"},
42
+ {"name": "get_algorithm_info", "category": "Documentation", "description": "Algorithm documentation"},
43
+ {"name": "list_hardware", "category": "Documentation", "description": "Available hardware profiles"},
44
+ {"name": "list_templates", "category": "Documentation", "description": "Available circuit templates"},
45
+ ]
46
+
47
+
48
+ def check_mcp_health(server_url: str = None) -> str:
49
+ """
50
+ Check overall MCP server health.
51
+
52
+ Args:
53
+ server_url: MCP server URL (uses default if not provided)
54
+
55
+ Returns:
56
+ Status string with emoji indicator
57
+ """
58
+ url = server_url or MCP_SERVER_URL
59
+ try:
60
+ response = requests.get(f"{url}/", timeout=10)
61
+ if response.status_code == 200:
62
+ return f"🟢 **Connected** to MCP Server"
63
+ else:
64
+ return f"🟡 **Partial** - Status {response.status_code}"
65
+ except requests.exceptions.Timeout:
66
+ return "🟠 **Timeout** - Server slow to respond"
67
+ except requests.exceptions.ConnectionError:
68
+ return "🔴 **Disconnected** - Cannot reach server"
69
+ except Exception as e:
70
+ return f"🔴 **Error**: {str(e)[:50]}"
71
+
72
+
73
+ def check_endpoint_health(endpoint_name: str, server_url: str = None) -> Dict:
74
+ """
75
+ Check health of a specific MCP endpoint.
76
+
77
+ Args:
78
+ endpoint_name: Name of the endpoint to check
79
+ server_url: MCP server URL (uses default if not provided)
80
+
81
+ Returns:
82
+ Dict with status, latency_ms, and error fields
83
+ """
84
+ url = server_url or MCP_SERVER_URL
85
+ start = time.perf_counter()
86
+ try:
87
+ endpoint_url = f"{url}/gradio_api/call/ui_{endpoint_name}"
88
+ response = requests.post(endpoint_url, json={"data": []}, timeout=15)
89
+ elapsed = (time.perf_counter() - start) * 1000
90
+
91
+ if response.status_code == 200:
92
+ return {"status": "🟢", "latency_ms": round(elapsed, 1), "error": None}
93
+ elif response.status_code == 404:
94
+ return {"status": "🟡", "latency_ms": round(elapsed, 1), "error": "Not found"}
95
+ else:
96
+ return {"status": "🟠", "latency_ms": round(elapsed, 1), "error": f"HTTP {response.status_code}"}
97
+ except requests.exceptions.Timeout:
98
+ return {"status": "🟠", "latency_ms": 15000, "error": "Timeout"}
99
+ except Exception as e:
100
+ elapsed = (time.perf_counter() - start) * 1000
101
+ return {"status": "🔴", "latency_ms": round(elapsed, 1), "error": str(e)[:50]}
102
+
103
+
104
+ def get_all_endpoints_health(server_url: str = None) -> str:
105
+ """
106
+ Get health status of all MCP endpoints as formatted markdown.
107
+
108
+ Args:
109
+ server_url: MCP server URL (uses default if not provided)
110
+
111
+ Returns:
112
+ Markdown formatted table with endpoint health status
113
+ """
114
+ url = server_url or MCP_SERVER_URL
115
+ output_lines = [
116
+ "## 🔗 MCP Endpoints Health Check",
117
+ f"**Server:** `{url}`\n",
118
+ "| Endpoint | Category | Status | Latency | Error |",
119
+ "|----------|----------|--------|---------|-------|"
120
+ ]
121
+
122
+ for endpoint in MCP_ENDPOINTS:
123
+ health = check_endpoint_health(endpoint["name"], url)
124
+ error_str = health["error"] or "-"
125
+ output_lines.append(
126
+ f"| `{endpoint['name']}` | {endpoint['category']} | {health['status']} | {health['latency_ms']}ms | {error_str} |"
127
+ )
128
+
129
+ output_lines.append(f"\n**Last checked:** {time.strftime('%Y-%m-%d %H:%M:%S UTC')}")
130
+ return "\n".join(output_lines)
131
+
132
+
133
+ def create_mcp_health_tab(server_url: str = None) -> gr.Markdown:
134
+ """
135
+ Create the MCP Health monitoring tab components.
136
+
137
+ Args:
138
+ server_url: MCP server URL (uses default if not provided)
139
+
140
+ Returns:
141
+ Health display Markdown component
142
+ """
143
+ url = server_url or MCP_SERVER_URL
144
+
145
+ gr.Markdown("""
146
+ ## 🔗 MCP Endpoints Health Monitor
147
+
148
+ Monitor the health and availability of QuantumArchitect-MCP endpoints.
149
+ """)
150
+
151
+ with gr.Row():
152
+ check_all_btn = gr.Button("🔄 Check All Endpoints", variant="primary")
153
+
154
+ health_display = gr.Markdown(value="Click 'Check All Endpoints' to start health check...")
155
+
156
+ gr.Markdown("---")
157
+ gr.Markdown("### 🔍 Check Single Endpoint")
158
+
159
+ with gr.Row():
160
+ endpoint_dropdown = gr.Dropdown(
161
+ choices=[ep["name"] for ep in MCP_ENDPOINTS],
162
+ label="Select Endpoint",
163
+ value=None,
164
+ scale=3
165
+ )
166
+ check_single_btn = gr.Button("Check", scale=1)
167
+
168
+ single_result = gr.Markdown(value="")
169
+
170
+ # Event handlers
171
+ def check_all_handler():
172
+ return get_all_endpoints_health(url)
173
+
174
+ def check_single_handler(endpoint_name: str) -> str:
175
+ if not endpoint_name:
176
+ return "Please select an endpoint."
177
+ health = check_endpoint_health(endpoint_name, url)
178
+ return f"**{endpoint_name}**: {health['status']} ({health['latency_ms']}ms) - {health['error'] or 'OK'}"
179
+
180
+ check_all_btn.click(check_all_handler, outputs=[health_display])
181
+ check_single_btn.click(check_single_handler, [endpoint_dropdown], [single_result])
182
+
183
+ return health_display
ui/quick_build.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
15
+ import gradio as gr
16
+ import logging
17
+ from typing import Optional
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ # Default MCP Server URL
22
+ MCP_SERVER_URL = os.environ.get(
23
+ "MCP_SERVER_URL",
24
+ "https://mcp-1st-birthday-quantumarchitect-mcp.hf.space"
25
+ )
26
+
27
+ # Available templates
28
+ CIRCUIT_TEMPLATES = [
29
+ "bell_state",
30
+ "ghz_state",
31
+ "qft",
32
+ "grover",
33
+ "superposition",
34
+ ]
35
+
36
+
37
+ def quick_build_circuit(template: str, num_qubits: int, server_url: Optional[str] = None) -> str:
38
+ """
39
+ Generate a circuit from template using MCP client.
40
+
41
+ Args:
42
+ template: Template name (bell_state, ghz_state, etc.)
43
+ num_qubits: Number of qubits
44
+ server_url: MCP server URL (uses default if not provided)
45
+
46
+ Returns:
47
+ QASM code or error message
48
+ """
49
+ url = server_url or MCP_SERVER_URL
50
+ try:
51
+ from client.mcp_client import get_client
52
+ mcp_client = get_client(url)
53
+ result = mcp_client.create_circuit_from_template(template, int(num_qubits))
54
+
55
+ if result.success and result.data:
56
+ if isinstance(result.data, dict) and 'qasm' in result.data:
57
+ return result.data['qasm']
58
+ return str(result.data)
59
+ return f"# Error: {result.error or 'Unknown error'}"
60
+ except Exception as e:
61
+ logger.error(f"Quick build error: {e}")
62
+ return f"# Error: {str(e)}"
63
+
64
+
65
+ def create_quick_build_tab(server_url: Optional[str] = None) -> gr.Code:
66
+ """
67
+ Create the Quick Build tab components.
68
+
69
+ Args:
70
+ server_url: MCP server URL (uses default if not provided)
71
+
72
+ Returns:
73
+ QASM output Code component
74
+ """
75
+ url = server_url or MCP_SERVER_URL
76
+
77
+ gr.Markdown("""
78
+ ## 🛠️ Quick Circuit Builder
79
+
80
+ Generate circuits directly from templates.
81
+ """)
82
+
83
+ with gr.Row():
84
+ with gr.Column():
85
+ template_select = gr.Dropdown(
86
+ choices=CIRCUIT_TEMPLATES,
87
+ value="bell_state",
88
+ label="Circuit Template"
89
+ )
90
+ qubits_slider = gr.Slider(
91
+ minimum=2,
92
+ maximum=8,
93
+ value=2,
94
+ step=1,
95
+ label="Number of Qubits"
96
+ )
97
+ build_btn = gr.Button("⚡ Generate Circuit", variant="primary")
98
+
99
+ with gr.Column():
100
+ qasm_output = gr.Code(
101
+ label="Generated QASM",
102
+ language="python",
103
+ lines=15
104
+ )
105
+
106
+ # Event handler
107
+ def build_handler(template: str, num_qubits: int) -> str:
108
+ return quick_build_circuit(template, num_qubits, url)
109
+
110
+ build_btn.click(build_handler, [template_select, qubits_slider], [qasm_output])
111
+
112
+ return qasm_output
ui/styles.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Path: QAgents-workflows/ui/styles.py
2
+ # Relations: Used by __init__.py, app.py
3
+ # Description: Custom CSS styles for the Gradio app interface
4
+ """
5
+ Styles Module: Custom CSS for QAgents Gradio interface.
6
+ Provides consistent styling across all UI components.
7
+ """
8
+
9
+ CUSTOM_CSS = """
10
+ /* Main container styling */
11
+ .gradio-container {
12
+ max-width: 1200px !important;
13
+ margin: auto !important;
14
+ }
15
+
16
+ /* Header styling */
17
+ .app-header {
18
+ text-align: center;
19
+ padding: 1rem;
20
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
21
+ border-radius: 10px;
22
+ margin-bottom: 1rem;
23
+ color: white;
24
+ }
25
+
26
+ /* Chat container */
27
+ .chat-container {
28
+ border: 1px solid #e0e0e0;
29
+ border-radius: 10px;
30
+ padding: 1rem;
31
+ }
32
+
33
+ /* Message styling */
34
+ .user-message {
35
+ background-color: #e3f2fd;
36
+ border-radius: 10px;
37
+ padding: 0.5rem 1rem;
38
+ margin: 0.25rem 0;
39
+ }
40
+
41
+ .bot-message {
42
+ background-color: #f5f5f5;
43
+ border-radius: 10px;
44
+ padding: 0.5rem 1rem;
45
+ margin: 0.25rem 0;
46
+ }
47
+
48
+ /* Code blocks in chat */
49
+ .code-block {
50
+ background-color: #1e1e1e;
51
+ color: #d4d4d4;
52
+ padding: 1rem;
53
+ border-radius: 5px;
54
+ font-family: 'Fira Code', 'Consolas', monospace;
55
+ overflow-x: auto;
56
+ }
57
+
58
+ /* Status indicators */
59
+ .status-connected {
60
+ color: #4caf50;
61
+ font-weight: bold;
62
+ }
63
+
64
+ .status-disconnected {
65
+ color: #f44336;
66
+ font-weight: bold;
67
+ }
68
+
69
+ .status-partial {
70
+ color: #ff9800;
71
+ font-weight: bold;
72
+ }
73
+
74
+ /* Health check table */
75
+ .health-table {
76
+ width: 100%;
77
+ border-collapse: collapse;
78
+ }
79
+
80
+ .health-table th, .health-table td {
81
+ padding: 0.5rem;
82
+ text-align: left;
83
+ border-bottom: 1px solid #e0e0e0;
84
+ }
85
+
86
+ /* Button styling */
87
+ .primary-btn {
88
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
89
+ border: none !important;
90
+ color: white !important;
91
+ }
92
+
93
+ .primary-btn:hover {
94
+ opacity: 0.9;
95
+ }
96
+
97
+ /* Tab styling */
98
+ .tab-nav {
99
+ border-bottom: 2px solid #667eea;
100
+ }
101
+
102
+ /* Circuit output box */
103
+ .circuit-output {
104
+ font-family: 'Fira Code', 'Consolas', monospace;
105
+ background-color: #f8f9fa;
106
+ border: 1px solid #e0e0e0;
107
+ border-radius: 5px;
108
+ padding: 1rem;
109
+ }
110
+
111
+ /* Loading indicator */
112
+ .loading {
113
+ display: inline-block;
114
+ width: 20px;
115
+ height: 20px;
116
+ border: 3px solid rgba(102, 126, 234, 0.3);
117
+ border-radius: 50%;
118
+ border-top-color: #667eea;
119
+ animation: spin 1s ease-in-out infinite;
120
+ }
121
+
122
+ @keyframes spin {
123
+ to { transform: rotate(360deg); }
124
+ }
125
+
126
+ /* Responsive adjustments */
127
+ @media (max-width: 768px) {
128
+ .gradio-container {
129
+ padding: 0.5rem !important;
130
+ }
131
+
132
+ .app-header h1 {
133
+ font-size: 1.5rem;
134
+ }
135
+ }
136
+ """
137
+
138
+ # Additional component-specific styles
139
+ CHAT_STYLES = """
140
+ .chatbot-container {
141
+ min-height: 400px;
142
+ max-height: 600px;
143
+ }
144
+ """
145
+
146
+ MCP_HEALTH_STYLES = """
147
+ .endpoint-card {
148
+ border: 1px solid #e0e0e0;
149
+ border-radius: 8px;
150
+ padding: 1rem;
151
+ margin: 0.5rem 0;
152
+ }
153
+ """
154
+
155
+ QUICK_BUILD_STYLES = """
156
+ .template-selector {
157
+ margin-bottom: 1rem;
158
+ }
159
+
160
+ .qubit-slider {
161
+ margin: 1rem 0;
162
+ }
163
+ """