Spaces:
Running
Running
owenkaplinsky
commited on
Commit
·
bce9c73
1
Parent(s):
0fc972f
Add mcp, gradio, and functions
Browse files- project/app.py +102 -26
- project/src/blocks/text.js +142 -8
- project/src/generators/python.js +151 -36
- project/src/index.js +2 -0
project/app.py
CHANGED
|
@@ -2,6 +2,7 @@ from fastapi import FastAPI, Request
|
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
import gradio as gr
|
| 4 |
import uvicorn
|
|
|
|
| 5 |
from dotenv import load_dotenv
|
| 6 |
|
| 7 |
app = FastAPI()
|
|
@@ -34,22 +35,78 @@ def execute_blockly_logic(user_inputs):
|
|
| 34 |
return "No Blockly code available"
|
| 35 |
|
| 36 |
result = ""
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
def capture_result(msg):
|
| 39 |
nonlocal result
|
| 40 |
result = msg
|
| 41 |
|
| 42 |
-
env = {
|
| 43 |
-
"reply": capture_result,
|
| 44 |
-
}
|
| 45 |
|
| 46 |
try:
|
| 47 |
-
exec(
|
| 48 |
if "create_mcp" in env:
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
elif "process_input" in env:
|
| 54 |
env["process_input"](user_inputs)
|
| 55 |
except Exception as e:
|
|
@@ -74,8 +131,13 @@ def build_interface():
|
|
| 74 |
txt = gr.Textbox(label=f"Input {i+1}", visible=False)
|
| 75 |
input_fields.append(txt)
|
| 76 |
input_group_items.append(txt)
|
|
|
|
|
|
|
| 77 |
|
| 78 |
-
|
|
|
|
|
|
|
|
|
|
| 79 |
|
| 80 |
with gr.Row():
|
| 81 |
submit_btn = gr.Button("Submit")
|
|
@@ -83,14 +145,12 @@ def build_interface():
|
|
| 83 |
|
| 84 |
def refresh_inputs():
|
| 85 |
global latest_blockly_code
|
| 86 |
-
|
| 87 |
-
# Parse the Python code to extract function parameters
|
| 88 |
import re
|
| 89 |
-
|
| 90 |
# Look for the create_mcp function definition
|
| 91 |
pattern = r'def create_mcp\((.*?)\):'
|
| 92 |
match = re.search(pattern, latest_blockly_code)
|
| 93 |
-
|
| 94 |
params = []
|
| 95 |
if match:
|
| 96 |
params_str = match.group(1)
|
|
@@ -109,38 +169,54 @@ def build_interface():
|
|
| 109 |
'name': param,
|
| 110 |
'type': 'str'
|
| 111 |
})
|
| 112 |
-
|
| 113 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
updates = []
|
| 115 |
for i, field in enumerate(input_fields):
|
| 116 |
if i < len(params):
|
| 117 |
-
# Show this field and update its label
|
| 118 |
param = params[i]
|
| 119 |
updates.append(gr.update(
|
| 120 |
visible=True,
|
| 121 |
-
label=f"{param['name']} ({param['type']})"
|
|
|
|
| 122 |
))
|
| 123 |
else:
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
return updates
|
| 128 |
|
| 129 |
def process_input(*args):
|
| 130 |
-
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
|
| 133 |
# When refresh is clicked, update input field visibility and labels
|
| 134 |
refresh_btn.click(
|
| 135 |
refresh_inputs,
|
| 136 |
-
outputs=input_fields,
|
| 137 |
queue=False
|
| 138 |
)
|
| 139 |
|
| 140 |
submit_btn.click(
|
| 141 |
process_input,
|
| 142 |
inputs=input_fields,
|
| 143 |
-
outputs=
|
| 144 |
)
|
| 145 |
|
| 146 |
return demo
|
|
|
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
import gradio as gr
|
| 4 |
import uvicorn
|
| 5 |
+
import re
|
| 6 |
from dotenv import load_dotenv
|
| 7 |
|
| 8 |
app = FastAPI()
|
|
|
|
| 35 |
return "No Blockly code available"
|
| 36 |
|
| 37 |
result = ""
|
| 38 |
+
|
| 39 |
+
code_lines = latest_blockly_code.splitlines()
|
| 40 |
+
|
| 41 |
+
cleaned_lines = []
|
| 42 |
+
skip = False
|
| 43 |
+
for line in code_lines:
|
| 44 |
+
stripped = line.strip()
|
| 45 |
+
|
| 46 |
+
# When we reach the Interface block, start skipping until after demo.launch()
|
| 47 |
+
if stripped.startswith("demo = gr.Interface("):
|
| 48 |
+
skip = True
|
| 49 |
+
continue
|
| 50 |
+
if skip:
|
| 51 |
+
# Stop skipping after demo.launch(...) line
|
| 52 |
+
if stripped.startswith("demo.launch("):
|
| 53 |
+
skip = False
|
| 54 |
+
continue
|
| 55 |
+
|
| 56 |
+
# Keep everything else
|
| 57 |
+
cleaned_lines.append(line)
|
| 58 |
+
|
| 59 |
+
# Remove trailing blank lines
|
| 60 |
+
while cleaned_lines and not cleaned_lines[-1].strip():
|
| 61 |
+
cleaned_lines.pop()
|
| 62 |
+
|
| 63 |
+
code_to_run = "\n".join(cleaned_lines)
|
| 64 |
+
|
| 65 |
+
if len(code_lines) > 7:
|
| 66 |
+
code_to_run = "\n".join(code_lines[:-7])
|
| 67 |
+
else:
|
| 68 |
+
code_to_run = "\n".join(code_lines)
|
| 69 |
+
|
| 70 |
def capture_result(msg):
|
| 71 |
nonlocal result
|
| 72 |
result = msg
|
| 73 |
|
| 74 |
+
env = {"reply": capture_result}
|
|
|
|
|
|
|
| 75 |
|
| 76 |
try:
|
| 77 |
+
exec(code_to_run, env)
|
| 78 |
if "create_mcp" in env:
|
| 79 |
+
import inspect
|
| 80 |
+
sig = inspect.signature(env["create_mcp"])
|
| 81 |
+
params = list(sig.parameters.values())
|
| 82 |
+
|
| 83 |
+
typed_args = []
|
| 84 |
+
for i, arg in enumerate(user_inputs):
|
| 85 |
+
if i >= len(params):
|
| 86 |
+
break
|
| 87 |
+
if arg is None or arg == "":
|
| 88 |
+
typed_args.append(None)
|
| 89 |
+
continue
|
| 90 |
+
|
| 91 |
+
anno = params[i].annotation
|
| 92 |
+
try:
|
| 93 |
+
# Convert using the declared type hint
|
| 94 |
+
if anno == int:
|
| 95 |
+
typed_args.append(int(arg))
|
| 96 |
+
elif anno == float:
|
| 97 |
+
typed_args.append(float(arg))
|
| 98 |
+
elif anno == bool:
|
| 99 |
+
typed_args.append(str(arg).lower() in ("true", "1"))
|
| 100 |
+
elif anno == str or anno == inspect._empty:
|
| 101 |
+
typed_args.append(str(arg))
|
| 102 |
+
else:
|
| 103 |
+
# Unknown or complex type — leave as-is
|
| 104 |
+
typed_args.append(arg)
|
| 105 |
+
except Exception:
|
| 106 |
+
# If conversion fails, pass the raw input
|
| 107 |
+
typed_args.append(arg)
|
| 108 |
+
|
| 109 |
+
result = env["create_mcp"](*typed_args)
|
| 110 |
elif "process_input" in env:
|
| 111 |
env["process_input"](user_inputs)
|
| 112 |
except Exception as e:
|
|
|
|
| 131 |
txt = gr.Textbox(label=f"Input {i+1}", visible=False)
|
| 132 |
input_fields.append(txt)
|
| 133 |
input_group_items.append(txt)
|
| 134 |
+
|
| 135 |
+
output_fields = []
|
| 136 |
|
| 137 |
+
with gr.Accordion("MCP Outputs", open=True):
|
| 138 |
+
for i in range(10):
|
| 139 |
+
out = gr.Textbox(label=f"Output {i+1}", visible=False, interactive=False)
|
| 140 |
+
output_fields.append(out)
|
| 141 |
|
| 142 |
with gr.Row():
|
| 143 |
submit_btn = gr.Button("Submit")
|
|
|
|
| 145 |
|
| 146 |
def refresh_inputs():
|
| 147 |
global latest_blockly_code
|
|
|
|
|
|
|
| 148 |
import re
|
| 149 |
+
|
| 150 |
# Look for the create_mcp function definition
|
| 151 |
pattern = r'def create_mcp\((.*?)\):'
|
| 152 |
match = re.search(pattern, latest_blockly_code)
|
| 153 |
+
|
| 154 |
params = []
|
| 155 |
if match:
|
| 156 |
params_str = match.group(1)
|
|
|
|
| 169 |
'name': param,
|
| 170 |
'type': 'str'
|
| 171 |
})
|
| 172 |
+
|
| 173 |
+
# Detect output count (out_amt = N)
|
| 174 |
+
out_amt_match = re.search(r'out_amt\s*=\s*(\d+)', latest_blockly_code)
|
| 175 |
+
out_amt = int(out_amt_match.group(1)) if out_amt_match else 0
|
| 176 |
+
|
| 177 |
+
# Update visibility + clear output fields
|
| 178 |
+
output_updates = []
|
| 179 |
+
for i, field in enumerate(output_fields):
|
| 180 |
+
if i < out_amt:
|
| 181 |
+
output_updates.append(gr.update(visible=True, label=f"Output {i+1}", value=""))
|
| 182 |
+
else:
|
| 183 |
+
output_updates.append(gr.update(visible=False, value=""))
|
| 184 |
+
|
| 185 |
+
# Update visibility + clear input fields
|
| 186 |
updates = []
|
| 187 |
for i, field in enumerate(input_fields):
|
| 188 |
if i < len(params):
|
|
|
|
| 189 |
param = params[i]
|
| 190 |
updates.append(gr.update(
|
| 191 |
visible=True,
|
| 192 |
+
label=f"{param['name']} ({param['type']})",
|
| 193 |
+
value=""
|
| 194 |
))
|
| 195 |
else:
|
| 196 |
+
updates.append(gr.update(visible=False, value=""))
|
| 197 |
+
|
| 198 |
+
return updates + output_updates
|
|
|
|
| 199 |
|
| 200 |
def process_input(*args):
|
| 201 |
+
result = execute_blockly_logic(args)
|
| 202 |
+
|
| 203 |
+
# If the result is a tuple or list, pad it to length 10
|
| 204 |
+
if isinstance(result, (tuple, list)):
|
| 205 |
+
return list(result) + [""] * (10 - len(result))
|
| 206 |
+
# If it's a single value, repeat it in the first slot and pad the rest
|
| 207 |
+
return [result] + [""] * 9
|
| 208 |
|
| 209 |
# When refresh is clicked, update input field visibility and labels
|
| 210 |
refresh_btn.click(
|
| 211 |
refresh_inputs,
|
| 212 |
+
outputs=input_fields + output_fields,
|
| 213 |
queue=False
|
| 214 |
)
|
| 215 |
|
| 216 |
submit_btn.click(
|
| 217 |
process_input,
|
| 218 |
inputs=input_fields,
|
| 219 |
+
outputs=output_fields
|
| 220 |
)
|
| 221 |
|
| 222 |
return demo
|
project/src/blocks/text.js
CHANGED
|
@@ -43,6 +43,9 @@ Blockly.Extensions.registerMutator(
|
|
| 43 |
this.inputNames_ = [];
|
| 44 |
this.inputTypes_ = [];
|
| 45 |
this.inputRefBlocks_ = new Map();
|
|
|
|
|
|
|
|
|
|
| 46 |
this.initialized_ = true;
|
| 47 |
// Mark all reference blocks with their owner for later identification
|
| 48 |
this._ownerBlockId = this.id;
|
|
@@ -57,6 +60,9 @@ Blockly.Extensions.registerMutator(
|
|
| 57 |
this.inputCount_ = this.inputCount_ || 0;
|
| 58 |
this.inputNames_ = this.inputNames_ || [];
|
| 59 |
this.inputTypes_ = this.inputTypes_ || [];
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
// Restore dynamically added input items
|
| 62 |
for (let i = 0; i < this.inputCount_; i++) {
|
|
@@ -76,6 +82,20 @@ Blockly.Extensions.registerMutator(
|
|
| 76 |
connection = itemBlock.nextConnection;
|
| 77 |
}
|
| 78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
return containerBlock;
|
| 80 |
},
|
| 81 |
|
|
@@ -85,7 +105,9 @@ Blockly.Extensions.registerMutator(
|
|
| 85 |
if (!this.initialized_) this.initialize();
|
| 86 |
|
| 87 |
const oldNames = [...(this.inputNames_ || [])];
|
|
|
|
| 88 |
const connections = [];
|
|
|
|
| 89 |
let itemBlock = containerBlock.getInputTargetBlock('STACK');
|
| 90 |
|
| 91 |
// Collect all child connections from mutator stack
|
|
@@ -94,10 +116,34 @@ Blockly.Extensions.registerMutator(
|
|
| 94 |
itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock();
|
| 95 |
}
|
| 96 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 97 |
const newCount = connections.length;
|
|
|
|
| 98 |
this.inputCount_ = newCount;
|
|
|
|
| 99 |
this.inputNames_ = this.inputNames_ || [];
|
| 100 |
this.inputTypes_ = this.inputTypes_ || [];
|
|
|
|
|
|
|
| 101 |
|
| 102 |
// Rebuild the new list of input names and types
|
| 103 |
let idx = 0;
|
|
@@ -111,6 +157,16 @@ Blockly.Extensions.registerMutator(
|
|
| 111 |
idx++;
|
| 112 |
}
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
// Dispose of removed input reference blocks when inputs shrink
|
| 115 |
if (newCount < oldNames.length) {
|
| 116 |
for (let i = newCount; i < oldNames.length; i++) {
|
|
@@ -182,9 +238,11 @@ Blockly.Extensions.registerMutator(
|
|
| 182 |
// Remove all dynamic and temporary inputs before reconstruction
|
| 183 |
let i = 0;
|
| 184 |
while (this.getInput('X' + i)) this.removeInput('X' + i++);
|
|
|
|
|
|
|
| 185 |
let t = 0;
|
| 186 |
while (this.getInput('T' + t)) this.removeInput('T' + t++);
|
| 187 |
-
['INPUTS_TEXT', 'TOOLS_TEXT'].forEach(name => {
|
| 188 |
if (this.getInput(name)) this.removeInput(name);
|
| 189 |
});
|
| 190 |
|
|
@@ -239,6 +297,39 @@ Blockly.Extensions.registerMutator(
|
|
| 239 |
}
|
| 240 |
}
|
| 241 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 242 |
this.workspace.render();
|
| 243 |
} finally {
|
| 244 |
Blockly.Events.enable();
|
|
@@ -250,6 +341,9 @@ Blockly.Extensions.registerMutator(
|
|
| 250 |
inputCount: this.inputCount_,
|
| 251 |
inputNames: this.inputNames_,
|
| 252 |
inputTypes: this.inputTypes_,
|
|
|
|
|
|
|
|
|
|
| 253 |
toolCount: this.toolCount_ || 0
|
| 254 |
};
|
| 255 |
},
|
|
@@ -258,6 +352,9 @@ Blockly.Extensions.registerMutator(
|
|
| 258 |
this.inputCount_ = state.inputCount;
|
| 259 |
this.inputNames_ = state.inputNames || [];
|
| 260 |
this.inputTypes_ = state.inputTypes || [];
|
|
|
|
|
|
|
|
|
|
| 261 |
this.toolCount_ = state.toolCount || 0;
|
| 262 |
}
|
| 263 |
},
|
|
@@ -268,10 +365,12 @@ Blockly.Extensions.registerMutator(
|
|
| 268 |
// Base block definitions
|
| 269 |
const container = {
|
| 270 |
type: "container",
|
| 271 |
-
message0: "inputs %1 %2",
|
| 272 |
args0: [
|
| 273 |
{ type: "input_dummy", name: "title" },
|
| 274 |
{ type: "input_statement", name: "STACK" },
|
|
|
|
|
|
|
| 275 |
],
|
| 276 |
colour: 160,
|
| 277 |
inputsInline: false
|
|
@@ -279,7 +378,27 @@ const container = {
|
|
| 279 |
|
| 280 |
const container_input = {
|
| 281 |
type: "container_input",
|
| 282 |
-
message0: "
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 283 |
args0: [
|
| 284 |
{
|
| 285 |
type: "field_dropdown",
|
|
@@ -320,11 +439,10 @@ const llm_call = {
|
|
| 320 |
|
| 321 |
const create_mcp = {
|
| 322 |
type: "create_mcp",
|
| 323 |
-
message0: "create MCP %1 %2
|
| 324 |
args0: [
|
| 325 |
{ type: "input_dummy" },
|
| 326 |
{ type: "input_statement", name: "BODY" },
|
| 327 |
-
{ type: "input_value", name: "RETURN" },
|
| 328 |
],
|
| 329 |
colour: 160,
|
| 330 |
inputsInline: true,
|
|
@@ -336,12 +454,11 @@ const create_mcp = {
|
|
| 336 |
|
| 337 |
const tool_def = {
|
| 338 |
type: "tool_def",
|
| 339 |
-
message0: "function %1 %2 %3
|
| 340 |
args0: [
|
| 341 |
{ type: "field_input", name: "NAME", text: "newFunction" },
|
| 342 |
{ type: "input_dummy" },
|
| 343 |
{ type: "input_statement", name: "BODY" },
|
| 344 |
-
{ type: "input_value", name: "RETURN" },
|
| 345 |
],
|
| 346 |
colour: 160,
|
| 347 |
inputsInline: true,
|
|
@@ -390,18 +507,35 @@ function generateUniqueToolName(workspace, excludeBlock) {
|
|
| 390 |
return name;
|
| 391 |
}
|
| 392 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 393 |
// Register tool_def block separately to include custom init logic
|
| 394 |
Blockly.Blocks['tool_def'] = {
|
| 395 |
init: function () {
|
| 396 |
this.jsonInit(tool_def);
|
| 397 |
// Apply extensions
|
| 398 |
Blockly.Extensions.apply('test_cleanup_extension', this, false);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 399 |
}
|
| 400 |
};
|
| 401 |
|
| 402 |
export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
|
| 403 |
-
create_mcp,
|
| 404 |
container,
|
| 405 |
container_input,
|
|
|
|
| 406 |
llm_call,
|
| 407 |
]);
|
|
|
|
| 43 |
this.inputNames_ = [];
|
| 44 |
this.inputTypes_ = [];
|
| 45 |
this.inputRefBlocks_ = new Map();
|
| 46 |
+
this.outputCount_ = 0;
|
| 47 |
+
this.outputNames_ = [];
|
| 48 |
+
this.outputTypes_ = [];
|
| 49 |
this.initialized_ = true;
|
| 50 |
// Mark all reference blocks with their owner for later identification
|
| 51 |
this._ownerBlockId = this.id;
|
|
|
|
| 60 |
this.inputCount_ = this.inputCount_ || 0;
|
| 61 |
this.inputNames_ = this.inputNames_ || [];
|
| 62 |
this.inputTypes_ = this.inputTypes_ || [];
|
| 63 |
+
this.outputCount_ = this.outputCount_ || 0;
|
| 64 |
+
this.outputNames_ = this.outputNames_ || [];
|
| 65 |
+
this.outputTypes_ = this.outputTypes_ || [];
|
| 66 |
|
| 67 |
// Restore dynamically added input items
|
| 68 |
for (let i = 0; i < this.inputCount_; i++) {
|
|
|
|
| 82 |
connection = itemBlock.nextConnection;
|
| 83 |
}
|
| 84 |
|
| 85 |
+
// Restore dynamically added output items
|
| 86 |
+
let connection2 = containerBlock.getInput('STACK2').connection;
|
| 87 |
+
for (let i = 0; i < this.outputCount_; i++) {
|
| 88 |
+
const itemBlock = workspace.newBlock('container_output');
|
| 89 |
+
itemBlock.initSvg();
|
| 90 |
+
const typeVal = this.outputTypes_[i] || 'string';
|
| 91 |
+
const nameVal = this.outputNames_[i] || typeVal;
|
| 92 |
+
itemBlock.setFieldValue(typeVal, 'TYPE');
|
| 93 |
+
itemBlock.setFieldValue(nameVal, 'NAME');
|
| 94 |
+
|
| 95 |
+
connection2.connect(itemBlock.previousConnection);
|
| 96 |
+
connection2 = itemBlock.nextConnection;
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
return containerBlock;
|
| 100 |
},
|
| 101 |
|
|
|
|
| 105 |
if (!this.initialized_) this.initialize();
|
| 106 |
|
| 107 |
const oldNames = [...(this.inputNames_ || [])];
|
| 108 |
+
const oldOutputNames = [...(this.outputNames_ || [])];
|
| 109 |
const connections = [];
|
| 110 |
+
const returnConnections = [];
|
| 111 |
let itemBlock = containerBlock.getInputTargetBlock('STACK');
|
| 112 |
|
| 113 |
// Collect all child connections from mutator stack
|
|
|
|
| 116 |
itemBlock = itemBlock.nextConnection && itemBlock.nextConnection.targetBlock();
|
| 117 |
}
|
| 118 |
|
| 119 |
+
// Save existing return connections before removing them
|
| 120 |
+
let rIdx = 0;
|
| 121 |
+
while (this.getInput('R' + rIdx)) {
|
| 122 |
+
const returnInput = this.getInput('R' + rIdx);
|
| 123 |
+
if (returnInput && returnInput.connection && returnInput.connection.targetConnection) {
|
| 124 |
+
returnConnections.push(returnInput.connection.targetConnection);
|
| 125 |
+
} else {
|
| 126 |
+
returnConnections.push(null);
|
| 127 |
+
}
|
| 128 |
+
rIdx++;
|
| 129 |
+
}
|
| 130 |
+
|
| 131 |
+
// Collect output specifications from STACK2
|
| 132 |
+
const outputSpecs = [];
|
| 133 |
+
let outputBlock = containerBlock.getInputTargetBlock('STACK2');
|
| 134 |
+
while (outputBlock) {
|
| 135 |
+
outputSpecs.push(outputBlock);
|
| 136 |
+
outputBlock = outputBlock.nextConnection && outputBlock.nextConnection.targetBlock();
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
const newCount = connections.length;
|
| 140 |
+
const newOutputCount = outputSpecs.length;
|
| 141 |
this.inputCount_ = newCount;
|
| 142 |
+
this.outputCount_ = newOutputCount;
|
| 143 |
this.inputNames_ = this.inputNames_ || [];
|
| 144 |
this.inputTypes_ = this.inputTypes_ || [];
|
| 145 |
+
this.outputNames_ = this.outputNames_ || [];
|
| 146 |
+
this.outputTypes_ = this.outputTypes_ || [];
|
| 147 |
|
| 148 |
// Rebuild the new list of input names and types
|
| 149 |
let idx = 0;
|
|
|
|
| 157 |
idx++;
|
| 158 |
}
|
| 159 |
|
| 160 |
+
// Rebuild the new list of output names and types
|
| 161 |
+
let oidx = 0;
|
| 162 |
+
const newOutputNames = [];
|
| 163 |
+
for (const outBlock of outputSpecs) {
|
| 164 |
+
this.outputTypes_[oidx] = outBlock.getFieldValue('TYPE') || 'string';
|
| 165 |
+
this.outputNames_[oidx] = outBlock.getFieldValue('NAME') || 'output' + oidx;
|
| 166 |
+
newOutputNames.push(this.outputNames_[oidx]);
|
| 167 |
+
oidx++;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
// Dispose of removed input reference blocks when inputs shrink
|
| 171 |
if (newCount < oldNames.length) {
|
| 172 |
for (let i = newCount; i < oldNames.length; i++) {
|
|
|
|
| 238 |
// Remove all dynamic and temporary inputs before reconstruction
|
| 239 |
let i = 0;
|
| 240 |
while (this.getInput('X' + i)) this.removeInput('X' + i++);
|
| 241 |
+
let r = 0;
|
| 242 |
+
while (this.getInput('R' + r)) this.removeInput('R' + r++);
|
| 243 |
let t = 0;
|
| 244 |
while (this.getInput('T' + t)) this.removeInput('T' + t++);
|
| 245 |
+
['INPUTS_TEXT', 'RETURNS_TEXT', 'TOOLS_TEXT'].forEach(name => {
|
| 246 |
if (this.getInput(name)) this.removeInput(name);
|
| 247 |
});
|
| 248 |
|
|
|
|
| 297 |
}
|
| 298 |
}
|
| 299 |
|
| 300 |
+
// Handle return inputs based on outputs
|
| 301 |
+
if (newOutputCount > 0) {
|
| 302 |
+
// Remove the default RETURN input if it exists
|
| 303 |
+
if (this.getInput('RETURN')) {
|
| 304 |
+
this.removeInput('RETURN');
|
| 305 |
+
}
|
| 306 |
+
|
| 307 |
+
// Add the "and return" label
|
| 308 |
+
const returnsText = this.appendDummyInput('RETURNS_TEXT');
|
| 309 |
+
returnsText.appendField('and return');
|
| 310 |
+
|
| 311 |
+
// Add each return value input slot
|
| 312 |
+
for (let j = 0; j < newOutputCount; j++) {
|
| 313 |
+
const type = this.outputTypes_[j] || 'string';
|
| 314 |
+
const name = this.outputNames_[j] || ('output' + j);
|
| 315 |
+
let check = null;
|
| 316 |
+
if (type === 'integer') check = 'Number';
|
| 317 |
+
if (type === 'string') check = 'String';
|
| 318 |
+
|
| 319 |
+
const returnInput = this.appendValueInput('R' + j);
|
| 320 |
+
if (check) returnInput.setCheck(check);
|
| 321 |
+
returnInput.appendField(type);
|
| 322 |
+
returnInput.appendField('"' + name + '":');
|
| 323 |
+
|
| 324 |
+
// Reconnect previous connection if it exists
|
| 325 |
+
if (returnConnections[j]) {
|
| 326 |
+
try {
|
| 327 |
+
returnInput.connection.connect(returnConnections[j]);
|
| 328 |
+
} catch { }
|
| 329 |
+
}
|
| 330 |
+
}
|
| 331 |
+
}
|
| 332 |
+
|
| 333 |
this.workspace.render();
|
| 334 |
} finally {
|
| 335 |
Blockly.Events.enable();
|
|
|
|
| 341 |
inputCount: this.inputCount_,
|
| 342 |
inputNames: this.inputNames_,
|
| 343 |
inputTypes: this.inputTypes_,
|
| 344 |
+
outputCount: this.outputCount_,
|
| 345 |
+
outputNames: this.outputNames_,
|
| 346 |
+
outputTypes: this.outputTypes_,
|
| 347 |
toolCount: this.toolCount_ || 0
|
| 348 |
};
|
| 349 |
},
|
|
|
|
| 352 |
this.inputCount_ = state.inputCount;
|
| 353 |
this.inputNames_ = state.inputNames || [];
|
| 354 |
this.inputTypes_ = state.inputTypes || [];
|
| 355 |
+
this.outputCount_ = state.outputCount || 0;
|
| 356 |
+
this.outputNames_ = state.outputNames || [];
|
| 357 |
+
this.outputTypes_ = state.outputTypes || [];
|
| 358 |
this.toolCount_ = state.toolCount || 0;
|
| 359 |
}
|
| 360 |
},
|
|
|
|
| 365 |
// Base block definitions
|
| 366 |
const container = {
|
| 367 |
type: "container",
|
| 368 |
+
message0: "inputs %1 %2 outputs %3 %4",
|
| 369 |
args0: [
|
| 370 |
{ type: "input_dummy", name: "title" },
|
| 371 |
{ type: "input_statement", name: "STACK" },
|
| 372 |
+
{ type: "input_dummy", name: "title2" },
|
| 373 |
+
{ type: "input_statement", name: "STACK2" },
|
| 374 |
],
|
| 375 |
colour: 160,
|
| 376 |
inputsInline: false
|
|
|
|
| 378 |
|
| 379 |
const container_input = {
|
| 380 |
type: "container_input",
|
| 381 |
+
message0: "%1 %2",
|
| 382 |
+
args0: [
|
| 383 |
+
{
|
| 384 |
+
type: "field_dropdown",
|
| 385 |
+
name: "TYPE",
|
| 386 |
+
options: [
|
| 387 |
+
["String", "string"],
|
| 388 |
+
["Integer", "integer"],
|
| 389 |
+
["List", "list"],
|
| 390 |
+
]
|
| 391 |
+
},
|
| 392 |
+
{ type: "field_input", name: "NAME" },
|
| 393 |
+
],
|
| 394 |
+
previousStatement: null,
|
| 395 |
+
nextStatement: null,
|
| 396 |
+
colour: 210,
|
| 397 |
+
};
|
| 398 |
+
|
| 399 |
+
const container_output = {
|
| 400 |
+
type: "container_output",
|
| 401 |
+
message0: "%1 %2",
|
| 402 |
args0: [
|
| 403 |
{
|
| 404 |
type: "field_dropdown",
|
|
|
|
| 439 |
|
| 440 |
const create_mcp = {
|
| 441 |
type: "create_mcp",
|
| 442 |
+
message0: "create MCP %1 %2",
|
| 443 |
args0: [
|
| 444 |
{ type: "input_dummy" },
|
| 445 |
{ type: "input_statement", name: "BODY" },
|
|
|
|
| 446 |
],
|
| 447 |
colour: 160,
|
| 448 |
inputsInline: true,
|
|
|
|
| 454 |
|
| 455 |
const tool_def = {
|
| 456 |
type: "tool_def",
|
| 457 |
+
message0: "function %1 %2 %3",
|
| 458 |
args0: [
|
| 459 |
{ type: "field_input", name: "NAME", text: "newFunction" },
|
| 460 |
{ type: "input_dummy" },
|
| 461 |
{ type: "input_statement", name: "BODY" },
|
|
|
|
| 462 |
],
|
| 463 |
colour: 160,
|
| 464 |
inputsInline: true,
|
|
|
|
| 507 |
return name;
|
| 508 |
}
|
| 509 |
|
| 510 |
+
// Register create_mcp block separately to include custom init logic
|
| 511 |
+
Blockly.Blocks['create_mcp'] = {
|
| 512 |
+
init: function () {
|
| 513 |
+
this.jsonInit(create_mcp);
|
| 514 |
+
// Apply extensions
|
| 515 |
+
Blockly.Extensions.apply('test_cleanup_extension', this, false);
|
| 516 |
+
// Initialize mutator state
|
| 517 |
+
if (this.initialize) {
|
| 518 |
+
this.initialize();
|
| 519 |
+
}
|
| 520 |
+
}
|
| 521 |
+
};
|
| 522 |
+
|
| 523 |
// Register tool_def block separately to include custom init logic
|
| 524 |
Blockly.Blocks['tool_def'] = {
|
| 525 |
init: function () {
|
| 526 |
this.jsonInit(tool_def);
|
| 527 |
// Apply extensions
|
| 528 |
Blockly.Extensions.apply('test_cleanup_extension', this, false);
|
| 529 |
+
// Initialize mutator state
|
| 530 |
+
if (this.initialize) {
|
| 531 |
+
this.initialize();
|
| 532 |
+
}
|
| 533 |
}
|
| 534 |
};
|
| 535 |
|
| 536 |
export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
|
|
|
|
| 537 |
container,
|
| 538 |
container_input,
|
| 539 |
+
container_output,
|
| 540 |
llm_call,
|
| 541 |
]);
|
project/src/generators/python.js
CHANGED
|
@@ -32,44 +32,107 @@ forBlock['create_mcp'] = function (block, generator) {
|
|
| 32 |
i++;
|
| 33 |
}
|
| 34 |
|
| 35 |
-
//
|
| 36 |
-
let toolDefs = [];
|
| 37 |
-
let t = 0;
|
| 38 |
-
while (block.getInput('T' + t)) {
|
| 39 |
-
const toolCode = generator.valueToCode(block, 'T' + t, Order.NONE);
|
| 40 |
-
if (toolCode) {
|
| 41 |
-
toolDefs.push(toolCode);
|
| 42 |
-
}
|
| 43 |
-
t++;
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
// Main function body and return value
|
| 47 |
let body = generator.statementToCode(block, 'BODY');
|
| 48 |
-
let
|
| 49 |
|
| 50 |
-
|
| 51 |
-
if (returnValue && block.inputNames_) {
|
| 52 |
-
for (let j = 0; j < block.inputNames_.length; j++) {
|
| 53 |
-
const paramName = block.inputNames_[j];
|
| 54 |
-
returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
|
| 55 |
-
}
|
| 56 |
-
}
|
| 57 |
|
| 58 |
-
|
| 59 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
}
|
|
|
|
| 65 |
|
| 66 |
// Create the main function definition
|
| 67 |
if (typedInputs.length > 0) {
|
| 68 |
-
code += `def create_mcp(${typedInputs.join(', ')}):\n${body}${returnStatement}\n`;
|
| 69 |
} else {
|
| 70 |
-
code += `def create_mcp():\n${body}${returnStatement}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
}
|
| 72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
return code;
|
| 74 |
};
|
| 75 |
|
|
@@ -105,24 +168,76 @@ forBlock['tool_def'] = function (block, generator) {
|
|
| 105 |
}
|
| 106 |
|
| 107 |
let body = generator.statementToCode(block, 'BODY');
|
| 108 |
-
let
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
|
|
|
|
|
|
| 115 |
}
|
| 116 |
-
}
|
| 117 |
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
|
| 120 |
// Construct the function definition
|
| 121 |
let code;
|
| 122 |
if (typedInputs.length > 0) {
|
| 123 |
code = `def ${name}(${typedInputs.join(', ')}):\n${body}${returnStatement}`;
|
| 124 |
} else {
|
| 125 |
-
code = `def ${name}():\n${body}${returnStatement}`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
}
|
| 127 |
|
| 128 |
// Return function definition as a string value (not executed immediately)
|
|
|
|
| 32 |
i++;
|
| 33 |
}
|
| 34 |
|
| 35 |
+
// Main function body and return value(s)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
let body = generator.statementToCode(block, 'BODY');
|
| 37 |
+
let returnStatement = '';
|
| 38 |
|
| 39 |
+
const returnValues = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
+
// Check if we have outputs defined and the R inputs exist
|
| 42 |
+
if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) {
|
| 43 |
+
for (let r = 0; r < block.outputCount_; r++) {
|
| 44 |
+
let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC);
|
| 45 |
+
|
| 46 |
+
// Replace placeholder args with actual names
|
| 47 |
+
if (returnValue && block.inputNames_) {
|
| 48 |
+
for (let j = 0; j < block.inputNames_.length; j++) {
|
| 49 |
+
const paramName = block.inputNames_[j];
|
| 50 |
+
returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
|
| 54 |
+
// Type-cast numeric returns to ensure proper Gradio compatibility
|
| 55 |
+
const outputType = block.outputTypes_[r] || 'string';
|
| 56 |
+
if (outputType === 'integer' && returnValue) {
|
| 57 |
+
returnValue = `int(${returnValue})`;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
returnValues.push(returnValue || 'None');
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
if (returnValues.length === 1) {
|
| 64 |
+
returnStatement = ` return ${returnValues[0]}\n`;
|
| 65 |
+
} else {
|
| 66 |
+
returnStatement = ` return (${returnValues.join(', ')})\n`;
|
| 67 |
+
}
|
| 68 |
+
} else {
|
| 69 |
+
// No outputs defined, return empty dict for MCP
|
| 70 |
+
returnStatement = ' return {}\n';
|
| 71 |
}
|
| 72 |
+
let code = '';
|
| 73 |
|
| 74 |
// Create the main function definition
|
| 75 |
if (typedInputs.length > 0) {
|
| 76 |
+
code += `def create_mcp(${typedInputs.join(', ')}):\n out_amt = ${returnValues.length}\n\n${body}${returnStatement}\n`;
|
| 77 |
} else {
|
| 78 |
+
code += `def create_mcp():\n out_amt = ${returnValues.length}\n\n${body || ''}${returnStatement}`;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
// Map Python types to Gradio components for inputs
|
| 82 |
+
const gradioInputs = [];
|
| 83 |
+
if (block.inputTypes_) {
|
| 84 |
+
for (let k = 0; k < block.inputTypes_.length; k++) {
|
| 85 |
+
const type = block.inputTypes_[k];
|
| 86 |
+
switch (type) {
|
| 87 |
+
case 'integer':
|
| 88 |
+
gradioInputs.push('gr.Number()');
|
| 89 |
+
break;
|
| 90 |
+
case 'string':
|
| 91 |
+
gradioInputs.push('gr.Textbox()');
|
| 92 |
+
break;
|
| 93 |
+
case 'list':
|
| 94 |
+
gradioInputs.push('gr.Dataframe()');
|
| 95 |
+
break;
|
| 96 |
+
default:
|
| 97 |
+
gradioInputs.push('gr.Textbox()');
|
| 98 |
+
}
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
// Map Python types to Gradio components for outputs
|
| 103 |
+
const gradioOutputs = [];
|
| 104 |
+
// Only add outputs if they actually exist in the block (R0, R1, etc.)
|
| 105 |
+
if (block.outputTypes_ && block.outputCount_ > 0 && block.getInput('R0')) {
|
| 106 |
+
// Use outputCount_ to ensure we only process actual outputs
|
| 107 |
+
for (let k = 0; k < block.outputCount_; k++) {
|
| 108 |
+
const type = block.outputTypes_[k];
|
| 109 |
+
switch (type) {
|
| 110 |
+
case 'integer':
|
| 111 |
+
gradioOutputs.push('gr.Number()');
|
| 112 |
+
break;
|
| 113 |
+
case 'string':
|
| 114 |
+
gradioOutputs.push('gr.Textbox()');
|
| 115 |
+
break;
|
| 116 |
+
case 'list':
|
| 117 |
+
gradioOutputs.push('gr.Dataframe()');
|
| 118 |
+
break;
|
| 119 |
+
default:
|
| 120 |
+
gradioOutputs.push('gr.Textbox()');
|
| 121 |
+
}
|
| 122 |
+
}
|
| 123 |
}
|
| 124 |
|
| 125 |
+
// Create Gradio Interface with dynamic I/O
|
| 126 |
+
// Always include outputs parameter, use empty list if no outputs
|
| 127 |
+
code += `\ndemo = gr.Interface(
|
| 128 |
+
fn=create_mcp,
|
| 129 |
+
inputs=[${gradioInputs.join(', ')}],
|
| 130 |
+
outputs=[${gradioOutputs.join(', ')}],
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
demo.launch(mcp_server=True)
|
| 134 |
+
`;
|
| 135 |
+
|
| 136 |
return code;
|
| 137 |
};
|
| 138 |
|
|
|
|
| 168 |
}
|
| 169 |
|
| 170 |
let body = generator.statementToCode(block, 'BODY');
|
| 171 |
+
let returnStatement = '';
|
| 172 |
+
|
| 173 |
+
// Check if we have outputs defined and the R inputs exist
|
| 174 |
+
if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) {
|
| 175 |
+
const returnValues = [];
|
| 176 |
+
for (let r = 0; r < block.outputCount_; r++) {
|
| 177 |
+
let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC);
|
| 178 |
+
|
| 179 |
+
// Replace placeholder args with actual names
|
| 180 |
+
if (returnValue && block.inputNames_) {
|
| 181 |
+
for (let j = 0; j < block.inputNames_.length; j++) {
|
| 182 |
+
const paramName = block.inputNames_[j];
|
| 183 |
+
returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName);
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
|
| 187 |
+
// Type-cast numeric returns to ensure proper Gradio compatibility
|
| 188 |
+
const outputType = block.outputTypes_[r] || 'string';
|
| 189 |
+
if (outputType === 'integer' && returnValue) {
|
| 190 |
+
returnValue = `int(${returnValue})`;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
returnValues.push(returnValue || 'None');
|
| 194 |
}
|
|
|
|
| 195 |
|
| 196 |
+
if (returnValues.length === 1) {
|
| 197 |
+
returnStatement = ` return ${returnValues[0]}\n`;
|
| 198 |
+
} else {
|
| 199 |
+
returnStatement = ` return (${returnValues.join(', ')})\n`;
|
| 200 |
+
}
|
| 201 |
+
} else {
|
| 202 |
+
// No outputs defined, add pass only if body is empty
|
| 203 |
+
if (!body || body.trim() === '') {
|
| 204 |
+
returnStatement = ' pass\n';
|
| 205 |
+
} else {
|
| 206 |
+
returnStatement = '';
|
| 207 |
+
}
|
| 208 |
+
}
|
| 209 |
|
| 210 |
// Construct the function definition
|
| 211 |
let code;
|
| 212 |
if (typedInputs.length > 0) {
|
| 213 |
code = `def ${name}(${typedInputs.join(', ')}):\n${body}${returnStatement}`;
|
| 214 |
} else {
|
| 215 |
+
code = `def ${name}():\n${body || ' pass\n'}${returnStatement}`;
|
| 216 |
+
}
|
| 217 |
+
|
| 218 |
+
// Add output type hints as comments if outputs are defined
|
| 219 |
+
if (block.outputTypes_ && block.outputTypes_.length > 0) {
|
| 220 |
+
let outputTypes = [];
|
| 221 |
+
for (let k = 0; k < block.outputTypes_.length; k++) {
|
| 222 |
+
const type = block.outputTypes_[k];
|
| 223 |
+
const outName = block.outputNames_[k] || ('output' + k);
|
| 224 |
+
let pyType;
|
| 225 |
+
switch (type) {
|
| 226 |
+
case 'integer':
|
| 227 |
+
pyType = 'int';
|
| 228 |
+
break;
|
| 229 |
+
case 'string':
|
| 230 |
+
pyType = 'str';
|
| 231 |
+
break;
|
| 232 |
+
case 'list':
|
| 233 |
+
pyType = 'list';
|
| 234 |
+
break;
|
| 235 |
+
default:
|
| 236 |
+
pyType = 'Any';
|
| 237 |
+
}
|
| 238 |
+
outputTypes.push(`${outName}: ${pyType}`);
|
| 239 |
+
}
|
| 240 |
+
code = code.slice(0, -1) + ` # Returns: ${outputTypes.join(', ')}\n`;
|
| 241 |
}
|
| 242 |
|
| 243 |
// Return function definition as a string value (not executed immediately)
|
project/src/index.js
CHANGED
|
@@ -78,6 +78,8 @@ const updateCode = () => {
|
|
| 78 |
code = call + code;
|
| 79 |
}
|
| 80 |
|
|
|
|
|
|
|
| 81 |
if (codeEl) {
|
| 82 |
codeEl.textContent = code;
|
| 83 |
}
|
|
|
|
| 78 |
code = call + code;
|
| 79 |
}
|
| 80 |
|
| 81 |
+
code = "import gradio as gr\n\n" + code
|
| 82 |
+
|
| 83 |
if (codeEl) {
|
| 84 |
codeEl.textContent = code;
|
| 85 |
}
|