Spaces:
Runtime error
Runtime error
| #!/usr/bin/env python3 | |
| """ | |
| Gradio GUI Interface for QuLab Infinite MCP Server | |
| Provides interactive web interface for scientific experimentation | |
| """ | |
| import gradio as gr | |
| import json | |
| from typing import Dict, List, Any, Optional | |
| import traceback | |
| # Initialize MCP server | |
| mcp_server = None | |
| mcp_error = None | |
| try: | |
| from qulab_mcp_server import QuLabMCPServer | |
| mcp_server = QuLabMCPServer() | |
| mcp_server.initialize() | |
| print("β MCP Server initialized successfully for GUI") | |
| except Exception as e: | |
| mcp_error = str(e) | |
| print(f"β MCP Server initialization failed: {e}") | |
| def format_tool_info(tools: Dict) -> str: | |
| """Format tool information for display""" | |
| if not tools: | |
| return "No tools available" | |
| info = [] | |
| for name, tool_info in tools.items(): | |
| tool_type = tool_info.get('type', 'unknown') | |
| description = tool_info.get('description', 'No description') | |
| # Add error margin info based on tool type | |
| if tool_type == 'experiment': | |
| error_info = " (Β±5-25% error margin)" | |
| elif 'real_algorithm' in description.lower(): | |
| error_info = " (Β±1-3% error margin)" | |
| else: | |
| error_info = " (Β±3-15% error margin)" | |
| info.append(f"π¬ **{name}**\n {description}{error_info}") | |
| return "\n\n".join(info) | |
| def get_tool_categories(): | |
| """Get categorized tool information""" | |
| if not mcp_server: | |
| return {"error": "MCP server not available"} | |
| categories = { | |
| "lab_tools": {}, | |
| "experiment_tools": {} | |
| } | |
| for name, tool in mcp_server.tools.items(): | |
| categories["lab_tools"][name] = { | |
| "description": tool.description, | |
| "type": "lab" | |
| } | |
| for name, tool in mcp_server.experiment_tools.items(): | |
| categories["experiment_tools"][name] = { | |
| "description": tool.description, | |
| "type": "experiment" | |
| } | |
| return categories | |
| def search_tools(query: str, category: str) -> str: | |
| """Search tools by query and category""" | |
| if not mcp_server: | |
| return "β MCP server not available" | |
| if not query.strip(): | |
| return "Please enter a search query" | |
| results = [] | |
| all_tools = {} | |
| if category in ["all", "lab"]: | |
| all_tools.update(mcp_server.tools) | |
| if category in ["all", "experiment"]: | |
| all_tools.update(mcp_server.experiment_tools) | |
| query_lower = query.lower() | |
| for name, tool in all_tools.items(): | |
| if (query_lower in name.lower() or | |
| query_lower in tool.description.lower()): | |
| tool_type = "experiment" if name.startswith("experiment.") else "lab" | |
| results.append(f"π¬ **{name}** ({tool_type})\n {tool.description}") | |
| if not results: | |
| return f"No tools found matching '{query}' in category '{category}'" | |
| return f"Found {len(results)} tools:\n\n" + "\n\n".join(results) | |
| def execute_tool(tool_name: str, parameters: str) -> str: | |
| """Execute a tool with given parameters""" | |
| if not mcp_server: | |
| return "β MCP server not available" | |
| try: | |
| # Parse parameters | |
| if parameters.strip(): | |
| try: | |
| params = json.loads(parameters) | |
| except json.JSONDecodeError: | |
| return "β Invalid JSON parameters. Please check your input format." | |
| else: | |
| params = {} | |
| # Find the tool | |
| all_tools = {**mcp_server.tools, **mcp_server.experiment_tools} | |
| if tool_name not in all_tools: | |
| return f"β Tool '{tool_name}' not found" | |
| tool = all_tools[tool_name] | |
| # Execute the tool using MCP request | |
| import asyncio | |
| from qulab_mcp_server import MCPRequest | |
| # Create MCP request | |
| request = MCPRequest( | |
| request_id=f"gradio_{tool_name}_{hash(str(params))}", | |
| tool=tool_name, | |
| parameters=params | |
| ) | |
| # Execute asynchronously | |
| try: | |
| loop = asyncio.new_event_loop() | |
| asyncio.set_event_loop(loop) | |
| response = loop.run_until_complete(mcp_server.execute_tool(request)) | |
| loop.close() | |
| # Format result with header | |
| result_text = f"π§ͺ **Execution Result for {tool_name}**\n\n" | |
| if response.status == 'success': | |
| result = response.result | |
| # Add error margin information based on tool type | |
| tool_type = "experiment" if tool_name.startswith("experiment.") else "lab" | |
| if tool_type == "experiment": | |
| result_text += "β οΈ **Note**: Experimental results have Β±5-25% margin of error\n\n" | |
| elif 'real_algorithm' in tool.description.lower(): | |
| result_text += "β οΈ **Note**: Real algorithm results have Β±1-3% margin of error\n\n" | |
| else: | |
| result_text += "β οΈ **Note**: Simulation results have Β±3-15% margin of error\n\n" | |
| # Format result for display | |
| if isinstance(result, dict): | |
| result_text += f"β **Success!**\n\n```json\n{json.dumps(result, indent=2)}\n```" | |
| else: | |
| result_text += f"β **Success!**\n\n**Result:** {str(result)}" | |
| else: | |
| result_text += f"β **Execution Failed**\n\n**Error:** {response.error or 'Unknown error'}\n**Status:** {response.status}" | |
| except Exception as e: | |
| result_text = f"π§ͺ **Execution Result for {tool_name}**\n\n" | |
| result_text += f"β **Critical Error**\n\n**Details:** {str(e)}\n\nPlease check tool name and parameters." | |
| return result_text | |
| except Exception as outer_e: | |
| return f"π§ͺ **Execution Result for {tool_name}**\n\nβ **Setup Error**\n\n**Details:** {str(outer_e)}" | |
| except Exception as e: | |
| return f"β Execution failed: {str(e)}\n\nTraceback:\n{traceback.format_exc()}" | |
| def get_experiment_protocol(experiment_id: str) -> str: | |
| """Get detailed protocol for an experiment""" | |
| if not mcp_server: | |
| return "β MCP server not available" | |
| try: | |
| protocol = mcp_server.get_experiment_protocol(experiment_id, include_detailed_steps=True) | |
| if "error" in protocol: | |
| return f"β {protocol['error']}" | |
| protocol_text = f"π§ͺ **Experiment Protocol: {experiment_id}**\n\n" | |
| protocol_text += f"**Title**: {protocol.get('title', 'N/A')}\n" | |
| protocol_text += f"**Overview**: {protocol.get('overview', 'N/A')}\n\n" | |
| if 'steps' in protocol: | |
| protocol_text += "**Detailed Steps:**\n" | |
| for i, step in enumerate(protocol['steps'], 1): | |
| protocol_text += f"{i}. **{step.get('description', 'N/A')}**\n" | |
| if 'duration' in step: | |
| protocol_text += f" β±οΈ Duration: {step['duration']}\n" | |
| if 'temperature' in step: | |
| protocol_text += f" π‘οΈ Temperature: {step['temperature']}\n" | |
| if 'safety_notes' in step: | |
| protocol_text += f" β οΈ Safety: {step['safety_notes']}\n" | |
| protocol_text += "\n" | |
| if 'safety_requirements' in protocol: | |
| protocol_text += "**π‘οΈ Safety Requirements:**\n" | |
| for req in protocol['safety_requirements']: | |
| protocol_text += f"β’ {req}\n" | |
| protocol_text += "\n" | |
| if 'equipment_needed' in protocol: | |
| protocol_text += "**π§ Equipment Needed:**\n" | |
| for eq in protocol['equipment_needed']: | |
| protocol_text += f"β’ {eq}\n" | |
| protocol_text += "\n" | |
| protocol_text += "β οΈ **Important**: This protocol is for educational/research purposes. Always follow laboratory safety guidelines and consult with qualified personnel." | |
| return protocol_text | |
| except Exception as e: | |
| return f"β Failed to get protocol: {str(e)}" | |
| def create_experiment_workflow(experiment_ids: str, workflow_name: str) -> str: | |
| """Create a workflow from multiple experiments""" | |
| if not mcp_server: | |
| return "β MCP server not available" | |
| try: | |
| # Parse experiment IDs | |
| exp_ids = [eid.strip() for eid in experiment_ids.split(',') if eid.strip()] | |
| if len(exp_ids) < 2: | |
| return "β Please provide at least 2 experiment IDs separated by commas" | |
| workflow = mcp_server.create_workflow_from_experiments(exp_ids, workflow_name or "Custom Workflow") | |
| if "error" in workflow: | |
| return f"β {workflow['error']}" | |
| workflow_text = f"π¬ **Workflow Created: {workflow_name}**\n\n" | |
| workflow_text += f"**Experiments**: {', '.join(exp_ids)}\n" | |
| workflow_text += f"**Steps**: {len(workflow.get('steps', []))}\n\n" | |
| if 'steps' in workflow: | |
| workflow_text += "**Workflow Steps:**\n" | |
| for i, step in enumerate(workflow['steps'], 1): | |
| workflow_text += f"{i}. {step.get('experiment_id', 'N/A')}\n" | |
| workflow_text += "\nβ Workflow ready for execution!" | |
| return workflow_text | |
| except Exception as e: | |
| return f"β Failed to create workflow: {str(e)}" | |
| # Create the Gradio interface | |
| def create_interface(): | |
| """Create the main Gradio interface""" | |
| with gr.Blocks(title="QuLab Infinite - Scientific Experimentation Platform", | |
| theme=gr.themes.Soft()) as interface: | |
| gr.Markdown(""" | |
| # 𧬠QuLab Infinite | |
| ## Universal Materials Science & Quantum Simulation Laboratory | |
| **The most comprehensive scientific experimentation platform ever created** | |
| Access **1,532+ scientific tools** across **220+ specialized laboratories** covering every conceivable type of experiment, reaction, analysis, and scientific process. | |
| """) | |
| if mcp_error: | |
| gr.Markdown(f"β οΈ **Warning**: MCP Server initialization failed: {mcp_error}") | |
| gr.Markdown("Some features may not be available. The API endpoints are still functional.") | |
| with gr.Tabs(): | |
| # Overview Tab | |
| with gr.TabItem("π Overview"): | |
| with gr.Row(): | |
| with gr.Column(scale=2): | |
| gr.Markdown(""" | |
| ## π Server Statistics | |
| - **Total Tools**: 1,532+ | |
| - **Laboratory Tools**: 1,506 | |
| - **Experiment Tools**: 26 | |
| - **Laboratories**: 220+ | |
| - **Scientific Domains**: 15+ major categories | |
| ## β οΈ Accuracy Guidelines | |
| - **Real Algorithms**: Β±1-3% error margin | |
| - **Simulations**: Β±3-15% error margin | |
| - **Experiments**: Β±5-25% error margin | |
| """) | |
| with gr.Column(scale=1): | |
| status_indicator = "π’ **Fully Operational**" if mcp_server else "π‘ **Limited Functionality**" | |
| gr.Markdown(f"### System Status\n{status_indicator}") | |
| if mcp_server: | |
| tool_count = len(mcp_server.tools) + len(mcp_server.experiment_tools) | |
| gr.Markdown(f"**Tools Available**: {tool_count}") | |
| # Tool Explorer Tab | |
| with gr.TabItem("π Tool Explorer"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| search_query = gr.Textbox( | |
| label="π Search Tools", | |
| placeholder="Enter keywords (e.g., 'organic', 'NMR', 'thermodynamics')", | |
| lines=1 | |
| ) | |
| category_filter = gr.Dropdown( | |
| choices=["all", "lab", "experiment"], | |
| value="all", | |
| label="π Category Filter" | |
| ) | |
| search_btn = gr.Button("π Search", variant="primary") | |
| with gr.Column(): | |
| tool_results = gr.Textbox( | |
| label="π Search Results", | |
| lines=15, | |
| interactive=False | |
| ) | |
| search_btn.click( | |
| fn=search_tools, | |
| inputs=[search_query, category_filter], | |
| outputs=tool_results, | |
| api_name="search_tools" | |
| ) | |
| # Quick tool list | |
| with gr.Accordion("π Complete Tool Catalog", open=False): | |
| tool_catalog = gr.Textbox( | |
| value=format_tool_info(get_tool_categories()) if mcp_server else "MCP server not available", | |
| label="All Available Tools", | |
| lines=20, | |
| interactive=False | |
| ) | |
| # Tool Execution Tab | |
| with gr.TabItem("β‘ Tool Execution"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| tool_name = gr.Textbox( | |
| label="π§ Tool Name", | |
| placeholder="Enter exact tool name (e.g., 'experiment.aldol_condensation')", | |
| lines=1 | |
| ) | |
| tool_params = gr.Textbox( | |
| label="βοΈ Parameters (JSON)", | |
| placeholder='{"temperature": 25, "concentration": 0.1}', | |
| lines=3 | |
| ) | |
| execute_btn = gr.Button("π Execute Tool", variant="primary", size="lg") | |
| with gr.Column(): | |
| execution_result = gr.Textbox( | |
| label="π Execution Result", | |
| lines=20, | |
| interactive=False | |
| ) | |
| execute_btn.click( | |
| fn=execute_tool, | |
| inputs=[tool_name, tool_params], | |
| outputs=execution_result, | |
| api_name="execute_tool" | |
| ) | |
| gr.Markdown(""" | |
| ### π‘ Usage Tips | |
| - **Tool Names**: Use exact names from the Tool Explorer | |
| - **Parameters**: JSON format, leave empty for default parameters | |
| - **Results**: Include error margins and confidence intervals | |
| - **Safety**: Experimental results are for research purposes only | |
| """) | |
| # Experiment Protocols Tab | |
| with gr.TabItem("π§ͺ Experiment Protocols"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| experiment_id = gr.Textbox( | |
| label="π§ͺ Experiment ID", | |
| placeholder="e.g., 'aldol_condensation', 'nmr_spectroscopy'", | |
| lines=1 | |
| ) | |
| protocol_btn = gr.Button("π Get Protocol", variant="secondary") | |
| with gr.Column(): | |
| protocol_display = gr.Textbox( | |
| label="π Detailed Protocol", | |
| lines=25, | |
| interactive=False | |
| ) | |
| protocol_btn.click( | |
| fn=get_experiment_protocol, | |
| inputs=[experiment_id], | |
| outputs=protocol_display, | |
| api_name="get_protocol" | |
| ) | |
| gr.Markdown(""" | |
| ### π Available Experiments | |
| - `aldol_condensation` - Aldol condensation reaction | |
| - `catalytic_hydrogenation` - Catalytic hydrogenation | |
| - `nmr_spectroscopy` - NMR spectroscopic analysis | |
| - `buffer_preparation` - Chemical buffer preparation | |
| - `recrystallization` - Purification by recrystallization | |
| """) | |
| # Workflow Composer Tab | |
| with gr.TabItem("π¬ Workflow Composer"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| workflow_experiments = gr.Textbox( | |
| label="π Experiment IDs", | |
| placeholder="e.g., aldol_condensation, recrystallization, nmr_spectroscopy", | |
| lines=2 | |
| ) | |
| workflow_name = gr.Textbox( | |
| label="π Workflow Name", | |
| placeholder="Custom Synthesis Workflow", | |
| lines=1 | |
| ) | |
| workflow_btn = gr.Button("π¬ Create Workflow", variant="secondary") | |
| with gr.Column(): | |
| workflow_result = gr.Textbox( | |
| label="π Workflow Result", | |
| lines=15, | |
| interactive=False | |
| ) | |
| workflow_btn.click( | |
| fn=create_experiment_workflow, | |
| inputs=[workflow_experiments, workflow_name], | |
| outputs=workflow_result, | |
| api_name="create_workflow" | |
| ) | |
| gr.Markdown(""" | |
| ### π Workflow Examples | |
| - **Organic Synthesis**: `aldol_condensation, recrystallization, nmr_spectroscopy` | |
| - **Material Analysis**: `alloy_synthesis, mechanical_testing, surface_analysis` | |
| - **Drug Discovery**: `molecular_docking, adme_prediction, toxicity_screening` | |
| """) | |
| return interface | |
| # Launch the interface | |
| if __name__ == "__main__": | |
| interface = create_interface() | |
| interface.launch( | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_api=True, # Enable Gradio API for external access | |
| share=False # Don't create public link for Spaces | |
| ) |