Spaces:
Running
Running
owenkaplinsky
commited on
Commit
·
2624895
1
Parent(s):
f429f61
Add lists_contains block; bool type
Browse files- project/blocks.txt +1 -0
- project/src/blocks/text.js +17 -0
- project/src/generators/python.js +27 -2
- project/src/index.js +0 -2
- project/src/toolbox.js +4 -0
- project/test.py +9 -4
project/blocks.txt
CHANGED
|
@@ -50,6 +50,7 @@ VALUE: lists_indexOf(inputs(END: "FIRST/LAST", VALUE: value, FIND: value))
|
|
| 50 |
VALUE: lists_reverse(inputs(LIST: value))
|
| 51 |
VALUE: lists_create_with(inputs(ADDN: value)) // N starts at 0; you can make as many N as you want
|
| 52 |
VALUE: lists_sort(inputs(TYPE: "NUMERIC/TEXT/IGNORE_CASE", DIRECTION: "1/-1")) // For direction, 1 is ascending, -1 is descending
|
|
|
|
| 53 |
|
| 54 |
# Variables
|
| 55 |
VALUE: variables_get(inputs(VAR: value)) // VAR is a variable ID
|
|
|
|
| 50 |
VALUE: lists_reverse(inputs(LIST: value))
|
| 51 |
VALUE: lists_create_with(inputs(ADDN: value)) // N starts at 0; you can make as many N as you want
|
| 52 |
VALUE: lists_sort(inputs(TYPE: "NUMERIC/TEXT/IGNORE_CASE", DIRECTION: "1/-1")) // For direction, 1 is ascending, -1 is descending
|
| 53 |
+
VALUE: lists_contains(inputs(ITEM: value, LIST: value))
|
| 54 |
|
| 55 |
# Variables
|
| 56 |
VALUE: variables_get(inputs(VAR: value)) // VAR is a variable ID
|
project/src/blocks/text.js
CHANGED
|
@@ -503,6 +503,7 @@ const container_input = {
|
|
| 503 |
["Integer", "integer"],
|
| 504 |
["Float", "float"],
|
| 505 |
["List", "list"],
|
|
|
|
| 506 |
]
|
| 507 |
},
|
| 508 |
{ type: "field_input", name: "NAME" },
|
|
@@ -524,6 +525,7 @@ const container_output = {
|
|
| 524 |
["Integer", "integer"],
|
| 525 |
["Float", "float"],
|
| 526 |
["List", "list"],
|
|
|
|
| 527 |
]
|
| 528 |
},
|
| 529 |
{ type: "field_input", name: "NAME" },
|
|
@@ -635,6 +637,20 @@ const make_json = {
|
|
| 635 |
fieldCount_: 1,
|
| 636 |
};
|
| 637 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 638 |
// Dynamic function call block
|
| 639 |
const func_call = {
|
| 640 |
type: "func_call",
|
|
@@ -1143,4 +1159,5 @@ export const blocks = Blockly.common.createBlockDefinitionsFromJsonArray([
|
|
| 1143 |
llm_call,
|
| 1144 |
call_api,
|
| 1145 |
in_json,
|
|
|
|
| 1146 |
]);
|
|
|
|
| 503 |
["Integer", "integer"],
|
| 504 |
["Float", "float"],
|
| 505 |
["List", "list"],
|
| 506 |
+
["Boolean", "boolean"],
|
| 507 |
]
|
| 508 |
},
|
| 509 |
{ type: "field_input", name: "NAME" },
|
|
|
|
| 525 |
["Integer", "integer"],
|
| 526 |
["Float", "float"],
|
| 527 |
["List", "list"],
|
| 528 |
+
["Boolean", "boolean"],
|
| 529 |
]
|
| 530 |
},
|
| 531 |
{ type: "field_input", name: "NAME" },
|
|
|
|
| 637 |
fieldCount_: 1,
|
| 638 |
};
|
| 639 |
|
| 640 |
+
const lists_contains = {
|
| 641 |
+
type: "lists_contains",
|
| 642 |
+
message0: "item %1 in list %2",
|
| 643 |
+
args0: [
|
| 644 |
+
{ type: "input_value", name: "ITEM", check: null },
|
| 645 |
+
{ type: "input_value", name: "LIST", check: "Array" },
|
| 646 |
+
],
|
| 647 |
+
output: "Boolean",
|
| 648 |
+
colour: 260,
|
| 649 |
+
inputsInline: true,
|
| 650 |
+
tooltip: "Check if an item exists in a list",
|
| 651 |
+
helpUrl: "",
|
| 652 |
+
};
|
| 653 |
+
|
| 654 |
// Dynamic function call block
|
| 655 |
const func_call = {
|
| 656 |
type: "func_call",
|
|
|
|
| 1159 |
llm_call,
|
| 1160 |
call_api,
|
| 1161 |
in_json,
|
| 1162 |
+
lists_contains,
|
| 1163 |
]);
|
project/src/generators/python.js
CHANGED
|
@@ -40,6 +40,9 @@ forBlock['create_mcp'] = function (block, generator) {
|
|
| 40 |
case 'list':
|
| 41 |
pyType = 'list';
|
| 42 |
break;
|
|
|
|
|
|
|
|
|
|
| 43 |
default:
|
| 44 |
pyType = 'Any';
|
| 45 |
}
|
|
@@ -67,12 +70,14 @@ forBlock['create_mcp'] = function (block, generator) {
|
|
| 67 |
}
|
| 68 |
}
|
| 69 |
|
| 70 |
-
// Type-cast
|
| 71 |
const outputType = block.outputTypes_[r] || 'string';
|
| 72 |
if (outputType === 'integer' && returnValue) {
|
| 73 |
returnValue = `int(${returnValue})`;
|
| 74 |
} else if (outputType === 'float' && returnValue) {
|
| 75 |
returnValue = `float(${returnValue})`;
|
|
|
|
|
|
|
| 76 |
}
|
| 77 |
|
| 78 |
returnValues.push(returnValue || 'None');
|
|
@@ -115,6 +120,9 @@ forBlock['create_mcp'] = function (block, generator) {
|
|
| 115 |
case 'list':
|
| 116 |
gradioInputs.push('gr.Dataframe()');
|
| 117 |
break;
|
|
|
|
|
|
|
|
|
|
| 118 |
default:
|
| 119 |
gradioInputs.push('gr.Textbox()');
|
| 120 |
}
|
|
@@ -141,6 +149,9 @@ forBlock['create_mcp'] = function (block, generator) {
|
|
| 141 |
case 'list':
|
| 142 |
gradioOutputs.push('gr.Dataframe()');
|
| 143 |
break;
|
|
|
|
|
|
|
|
|
|
| 144 |
default:
|
| 145 |
gradioOutputs.push('gr.Textbox()');
|
| 146 |
}
|
|
@@ -199,6 +210,9 @@ forBlock['func_def'] = function (block, generator) {
|
|
| 199 |
case 'list':
|
| 200 |
pyType = 'list';
|
| 201 |
break;
|
|
|
|
|
|
|
|
|
|
| 202 |
default:
|
| 203 |
pyType = 'Any';
|
| 204 |
}
|
|
@@ -224,12 +238,14 @@ forBlock['func_def'] = function (block, generator) {
|
|
| 224 |
}
|
| 225 |
}
|
| 226 |
|
| 227 |
-
// Type-cast
|
| 228 |
const outputType = block.outputTypes_[r] || 'string';
|
| 229 |
if (outputType === 'integer' && returnValue) {
|
| 230 |
returnValue = `int(${returnValue})`;
|
| 231 |
} else if (outputType === 'float' && returnValue) {
|
| 232 |
returnValue = `float(${returnValue})`;
|
|
|
|
|
|
|
| 233 |
}
|
| 234 |
|
| 235 |
returnValues.push(returnValue || 'None');
|
|
@@ -362,4 +378,13 @@ forBlock['make_json'] = function (block, generator) {
|
|
| 362 |
// Generate valid Python dict syntax
|
| 363 |
const code = pairs.length > 0 ? `{${pairs.join(', ')}}` : '{}';
|
| 364 |
return [code, Order.ATOMIC];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 365 |
};
|
|
|
|
| 40 |
case 'list':
|
| 41 |
pyType = 'list';
|
| 42 |
break;
|
| 43 |
+
case 'boolean':
|
| 44 |
+
pyType = 'bool';
|
| 45 |
+
break;
|
| 46 |
default:
|
| 47 |
pyType = 'Any';
|
| 48 |
}
|
|
|
|
| 70 |
}
|
| 71 |
}
|
| 72 |
|
| 73 |
+
// Type-cast returns to ensure proper Gradio compatibility
|
| 74 |
const outputType = block.outputTypes_[r] || 'string';
|
| 75 |
if (outputType === 'integer' && returnValue) {
|
| 76 |
returnValue = `int(${returnValue})`;
|
| 77 |
} else if (outputType === 'float' && returnValue) {
|
| 78 |
returnValue = `float(${returnValue})`;
|
| 79 |
+
} else if (outputType === 'boolean' && returnValue) {
|
| 80 |
+
returnValue = `bool(${returnValue})`;
|
| 81 |
}
|
| 82 |
|
| 83 |
returnValues.push(returnValue || 'None');
|
|
|
|
| 120 |
case 'list':
|
| 121 |
gradioInputs.push('gr.Dataframe()');
|
| 122 |
break;
|
| 123 |
+
case 'boolean':
|
| 124 |
+
gradioInputs.push('gr.Checkbox()');
|
| 125 |
+
break;
|
| 126 |
default:
|
| 127 |
gradioInputs.push('gr.Textbox()');
|
| 128 |
}
|
|
|
|
| 149 |
case 'list':
|
| 150 |
gradioOutputs.push('gr.Dataframe()');
|
| 151 |
break;
|
| 152 |
+
case 'boolean':
|
| 153 |
+
gradioOutputs.push('gr.Checkbox()');
|
| 154 |
+
break;
|
| 155 |
default:
|
| 156 |
gradioOutputs.push('gr.Textbox()');
|
| 157 |
}
|
|
|
|
| 210 |
case 'list':
|
| 211 |
pyType = 'list';
|
| 212 |
break;
|
| 213 |
+
case 'boolean':
|
| 214 |
+
pyType = 'bool';
|
| 215 |
+
break;
|
| 216 |
default:
|
| 217 |
pyType = 'Any';
|
| 218 |
}
|
|
|
|
| 238 |
}
|
| 239 |
}
|
| 240 |
|
| 241 |
+
// Type-cast returns to ensure proper Gradio compatibility
|
| 242 |
const outputType = block.outputTypes_[r] || 'string';
|
| 243 |
if (outputType === 'integer' && returnValue) {
|
| 244 |
returnValue = `int(${returnValue})`;
|
| 245 |
} else if (outputType === 'float' && returnValue) {
|
| 246 |
returnValue = `float(${returnValue})`;
|
| 247 |
+
} else if (outputType === 'boolean' && returnValue) {
|
| 248 |
+
returnValue = `bool(${returnValue})`;
|
| 249 |
}
|
| 250 |
|
| 251 |
returnValues.push(returnValue || 'None');
|
|
|
|
| 378 |
// Generate valid Python dict syntax
|
| 379 |
const code = pairs.length > 0 ? `{${pairs.join(', ')}}` : '{}';
|
| 380 |
return [code, Order.ATOMIC];
|
| 381 |
+
};
|
| 382 |
+
|
| 383 |
+
forBlock['lists_contains'] = function (block, generator) {
|
| 384 |
+
const item = generator.valueToCode(block, 'ITEM', Order.NONE) || "''";
|
| 385 |
+
const list = generator.valueToCode(block, 'LIST', Order.NONE) || "[]";
|
| 386 |
+
|
| 387 |
+
// Generate code to check if item is in list
|
| 388 |
+
const code = `${item} in ${list}`;
|
| 389 |
+
return [code, Order.ATOMIC];
|
| 390 |
};
|
project/src/index.js
CHANGED
|
@@ -1274,12 +1274,10 @@ const updateCode = () => {
|
|
| 1274 |
const blocks = ws.getAllBlocks(false);
|
| 1275 |
const hasCall = blocks.some(block => block.type === 'llm_call');
|
| 1276 |
const hasAPI = blocks.some(block => block.type === 'call_api');
|
| 1277 |
-
const hasListSort = code.includes('lists_sort(');
|
| 1278 |
const hasPrime = code.includes('math_isPrime(');
|
| 1279 |
|
| 1280 |
if (hasCall) code = call + code;
|
| 1281 |
if (hasAPI) code = API + code;
|
| 1282 |
-
if (hasListSort) code = listSort + code;
|
| 1283 |
|
| 1284 |
if (hasPrime) {
|
| 1285 |
code = code.replace(/math_isPrime\(([^)]*)\)/g, 'isprime($1)');
|
|
|
|
| 1274 |
const blocks = ws.getAllBlocks(false);
|
| 1275 |
const hasCall = blocks.some(block => block.type === 'llm_call');
|
| 1276 |
const hasAPI = blocks.some(block => block.type === 'call_api');
|
|
|
|
| 1277 |
const hasPrime = code.includes('math_isPrime(');
|
| 1278 |
|
| 1279 |
if (hasCall) code = call + code;
|
| 1280 |
if (hasAPI) code = API + code;
|
|
|
|
| 1281 |
|
| 1282 |
if (hasPrime) {
|
| 1283 |
code = code.replace(/math_isPrime\(([^)]*)\)/g, 'isprime($1)');
|
project/src/toolbox.js
CHANGED
|
@@ -518,6 +518,10 @@ export const toolbox = {
|
|
| 518 |
kind: 'block',
|
| 519 |
type: 'lists_reverse',
|
| 520 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
| 521 |
],
|
| 522 |
},
|
| 523 |
{
|
|
|
|
| 518 |
kind: 'block',
|
| 519 |
type: 'lists_reverse',
|
| 520 |
},
|
| 521 |
+
{
|
| 522 |
+
kind: 'block',
|
| 523 |
+
type: 'lists_contains',
|
| 524 |
+
},
|
| 525 |
],
|
| 526 |
},
|
| 527 |
{
|
project/test.py
CHANGED
|
@@ -155,7 +155,11 @@ def execute_blockly_logic(user_inputs):
|
|
| 155 |
elif anno == float:
|
| 156 |
typed_args.append(float(arg))
|
| 157 |
elif anno == bool:
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
elif anno == list:
|
| 160 |
try:
|
| 161 |
# Convert string like '["a", "b", "c"]' into an actual list
|
|
@@ -193,7 +197,7 @@ def execute_blockly_logic(user_inputs):
|
|
| 193 |
print("[EXECUTION ERROR]", e)
|
| 194 |
result = f"Error: {str(e)}"
|
| 195 |
|
| 196 |
-
return result if result else "No output generated"
|
| 197 |
|
| 198 |
|
| 199 |
def build_interface():
|
|
@@ -279,12 +283,13 @@ def build_interface():
|
|
| 279 |
out_types = []
|
| 280 |
else:
|
| 281 |
out_types = []
|
| 282 |
-
# Convert output types: handle string, integer, float, list
|
| 283 |
out_types = [
|
| 284 |
"string" if t == "str" else
|
| 285 |
"integer" if t == "int" else
|
| 286 |
"float" if t == "float" else
|
| 287 |
-
"list" if t == "list" else
|
|
|
|
| 288 |
for t in out_types
|
| 289 |
]
|
| 290 |
|
|
|
|
| 155 |
elif anno == float:
|
| 156 |
typed_args.append(float(arg))
|
| 157 |
elif anno == bool:
|
| 158 |
+
# Handle boolean conversion from string (checkbox in Gradio sends True/False)
|
| 159 |
+
if isinstance(arg, bool):
|
| 160 |
+
typed_args.append(arg)
|
| 161 |
+
else:
|
| 162 |
+
typed_args.append(str(arg).lower() in ("true", "1"))
|
| 163 |
elif anno == list:
|
| 164 |
try:
|
| 165 |
# Convert string like '["a", "b", "c"]' into an actual list
|
|
|
|
| 197 |
print("[EXECUTION ERROR]", e)
|
| 198 |
result = f"Error: {str(e)}"
|
| 199 |
|
| 200 |
+
return result if result is not None and result != "" else "No output generated"
|
| 201 |
|
| 202 |
|
| 203 |
def build_interface():
|
|
|
|
| 283 |
out_types = []
|
| 284 |
else:
|
| 285 |
out_types = []
|
| 286 |
+
# Convert output types: handle string, integer, float, list, boolean
|
| 287 |
out_types = [
|
| 288 |
"string" if t == "str" else
|
| 289 |
"integer" if t == "int" else
|
| 290 |
"float" if t == "float" else
|
| 291 |
+
"list" if t == "list" else
|
| 292 |
+
"boolean" if t == "bool" else t
|
| 293 |
for t in out_types
|
| 294 |
]
|
| 295 |
|