MCEPTION / src /core /builder /code_generator.py
Ali Hmaou
Version 1.94RC
a0a02f2
import textwrap
class CodeGenerator:
@staticmethod
def generate_gradio_app(function_code: str, inputs: dict, output_desc: str) -> str:
"""
Generates the complete code for a Gradio application from a function snippet.
Args:
function_code: The source code of the main function (e.g. def count_r(word): ...)
inputs: Dict describing inputs (e.g. {"word": "text"})
output_desc: Description of the output
Returns:
The complete source code for app.py
"""
# Simple analysis to find the function name (very naive for now)
# We assume the code contains "def function_name("
import re
match = re.search(r"def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(", function_code)
func_name = match.group(1) if match else "main_function"
# Mapping MCP/JSON types to Gradio types
# For simplicity, we map everything to Text for now or use direct strings
# TODO: Improve type mapping
# Code construction
template = f"""
import gradio as gr
import json
# --- User Defined Logic ---
{function_code}
# --- Gradio Interface ---
# Wrapper to handle types if necessary
def wrapper(*args):
result = {func_name}(*args)
return str(result) # Force string output for simplicity
# Gradio inputs configuration
# Note: This part is generic for the MVP.
# Ideally, iterate over 'inputs' to create corresponding Gradio components.
iface = gr.Interface(
fn=wrapper,
inputs=[gr.Textbox(label=k) for k in {list(inputs.keys())}],
outputs=gr.Textbox(label="{output_desc}"),
title="Meta-MCP Generated Tool",
description="Auto-generated by Meta-MCP Fractal"
)
if __name__ == "__main__":
iface.launch(mcp_server=True, show_error=True)
"""
return textwrap.dedent(template).strip()
@staticmethod
def generate_tool_module(function_code: str, inputs: dict, output_desc: str, tool_name: str, output_component: str = "text") -> str:
"""
Generates a Python module containing the tool logic and an interface factory.
"""
import re
match = re.search(r"def\s+([a-zA-Z_][a-zA-Z0-9_]*)\s*\(", function_code)
func_name = match.group(1) if match else "main_function"
inputs_keys_str = str(list(inputs.keys()))
# Output component mapping
if output_component == "image":
gradio_output = 'gr.Image(type="filepath", label="__OUTPUT_DESC__")'
elif output_component == "audio":
gradio_output = 'gr.Audio(type="filepath", label="__OUTPUT_DESC__")'
elif output_component == "video":
gradio_output = 'gr.Video(label="__OUTPUT_DESC__")'
elif output_component == "html":
gradio_output = 'gr.HTML(label="__OUTPUT_DESC__")'
elif output_component == "json":
gradio_output = 'gr.JSON(label="__OUTPUT_DESC__")'
elif output_component == "file":
gradio_output = 'gr.File(label="__OUTPUT_DESC__")'
else: # text
gradio_output = 'gr.Textbox(label="__OUTPUT_DESC__")'
template = """
import gradio as gr
import json
from PIL import Image
import numpy as np
# --- User Defined Logic ---
__FUNCTION_CODE__
# --- Interface Factory ---
def create_interface():
return gr.Interface(
fn=__FUNC_NAME__,
inputs=[gr.Textbox(label=k) for k in __INPUTS_KEYS__],
outputs=__GRADIO_OUTPUT__,
title="__TOOL_NAME__",
description="Auto-generated tool: __TOOL_NAME__"
)
"""
code = textwrap.dedent(template).strip()
code = code.replace("__FUNCTION_CODE__", function_code)
code = code.replace("__FUNC_NAME__", func_name)
code = code.replace("__INPUTS_KEYS__", inputs_keys_str)
code = code.replace("__GRADIO_OUTPUT__", gradio_output) # Dynamic component injection
code = code.replace("__OUTPUT_DESC__", output_desc)
code = code.replace("__TOOL_NAME__", tool_name)
return code
@staticmethod
def generate_master_app() -> str:
"""
Generates the main app.py file.
Uses a standard approach with simple dynamic import.
"""
template = """
import gradio as gr
import os
import sys
import importlib
# Configuration
TOOLS_DIR = "tools"
# Ensure tools directory exists and is a package
if not os.path.exists(TOOLS_DIR):
os.makedirs(TOOLS_DIR, exist_ok=True)
with open(os.path.join(TOOLS_DIR, "__init__.py"), "w") as f:
pass
# Add current directory to path so 'import tools.xxx' works
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
interfaces = []
names = []
print(f"πŸš€ Starting Meta-MCP Toolbox...")
print(f"πŸ“‚ Scanning '{TOOLS_DIR}' directory...")
# Scan and import tools
try:
for filename in sorted(os.listdir(TOOLS_DIR)):
if filename.endswith(".py") and not filename.startswith("_"):
module_name = filename[:-3]
full_module_name = f"{TOOLS_DIR}.{module_name}"
try:
print(f" πŸ‘‰ Importing {full_module_name}...")
# Standard dynamic import
# Use reload to ensure latest version is taken if restarted
module = importlib.import_module(full_module_name)
importlib.reload(module)
if hasattr(module, "create_interface"):
# Create Gradio interface for this tool
tool_interface = module.create_interface()
interfaces.append(tool_interface)
names.append(module_name)
print(f" βœ… Loaded {module_name}")
else:
print(f" ⚠️ Module {module_name} has no create_interface()")
except Exception as e:
print(f" ❌ Error loading {module_name}: {e}")
import traceback
traceback.print_exc()
except Exception as e:
print(f"Error scanning tools directory: {e}")
# Final interface construction
if not interfaces:
demo = gr.Interface(
fn=lambda x: "No tools loaded yet. Add a tool via Meta-MCP!",
inputs="text",
outputs="text",
title="Empty Toolbox",
description="This Space is ready to receive tools."
)
else:
demo = gr.TabbedInterface(interfaces, names)
if __name__ == "__main__":
demo.launch(mcp_server=True, show_error=True)
"""
return textwrap.dedent(template).strip()
@staticmethod
def generate_mcp_server_code(function_code: str) -> str:
"""
Generates an MCP server (FastMCP) instead of Gradio (Future feature).
"""
pass