import { Order } from 'blockly/python'; import * as Blockly from 'blockly'; export const forBlock = Object.create(null); forBlock['create_mcp'] = function (block, generator) { // Ensure the generator is properly initialized for nested blocks like loops if (!generator.nameDB_) { generator.nameDB_ = new Blockly.Names(generator.RESERVED_WORDS_ || []); } // Ensure getDistinctName is available for control flow blocks if (!generator.getDistinctName) { generator.getDistinctName = function (name, type) { return this.nameDB_.getDistinctName(name, type); }; } const typedInputs = []; const listParams = []; let i = 0; // Build list of typed input parameters with inferred Python types while (block.getInput('X' + i)) { const input = block.getInput('X' + i); if (input && input.connection && input.connection.targetBlock()) { const paramName = (block.inputNames_ && block.inputNames_[i]) || ('arg' + i); const type = (block.inputTypes_ && block.inputTypes_[i]) || 'string'; let pyType; switch (type) { case 'integer': pyType = 'int'; break; case 'float': pyType = 'float'; break; case 'string': pyType = 'str'; break; case 'list': pyType = 'list'; listParams.push(paramName); break; case 'boolean': pyType = 'bool'; break; default: pyType = 'Any'; } typedInputs.push(`${paramName}: ${pyType}`); } i++; } // Main function body and return value(s) let body = generator.statementToCode(block, 'BODY'); // Add list conversion code at the beginning of the body let listConversionCode = ''; for (const param of listParams) { listConversionCode += ` ${param} = ${param}.iloc[:, 0].tolist()\n`; } body = listConversionCode + body; let returnStatement = ''; const returnValues = []; // Check if we have outputs defined and the R inputs exist if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) { for (let r = 0; r < block.outputCount_; r++) { let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC); // Replace placeholder args with actual names if (returnValue && block.inputNames_) { for (let j = 0; j < block.inputNames_.length; j++) { const paramName = block.inputNames_[j]; returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName); } } // Type-cast returns to ensure proper Gradio compatibility const outputType = block.outputTypes_[r] || 'string'; if (outputType === 'integer' && returnValue) { returnValue = `int(${returnValue})`; } else if (outputType === 'float' && returnValue) { returnValue = `float(${returnValue})`; } else if (outputType === 'boolean' && returnValue) { returnValue = `bool(${returnValue})`; } returnValues.push(returnValue || 'None'); } if (returnValues.length === 1) { returnStatement = ` return ${returnValues[0]}\n`; } else { returnStatement = ` return (${returnValues.join(', ')})\n`; } } else { // No outputs defined, return empty dict for MCP returnStatement = ' return {}\n'; } let code = ''; // Create the main function definition if (typedInputs.length > 0) { code += `def create_mcp(${typedInputs.join(', ')}):\n out_amt = ${returnValues.length}\n out_names = ${JSON.stringify(block.outputNames_ || [])}\n out_types = ${JSON.stringify(block.outputTypes_ || [])}\n\n${body}${returnStatement}\n`; } else { code += `def create_mcp():\n out_amt = ${returnValues.length}\n out_names = ${JSON.stringify(block.outputNames_ || [])}\n out_types = ${JSON.stringify(block.outputTypes_ || [])}\n\n${body || ''}${returnStatement}`; } // Map Python types to Gradio components for inputs // Only add inputs if they actually exist in the block (X0, X1, etc.) const gradioInputs = []; if (block.inputCount_ && block.inputCount_ > 0 && block.getInput('X0')) { for (let k = 0; k < block.inputCount_; k++) { const type = block.inputTypes_[k]; switch (type) { case 'integer': gradioInputs.push('gr.Number()'); break; case 'float': gradioInputs.push('gr.Number()'); break; case 'string': gradioInputs.push('gr.Textbox()'); break; case 'list': gradioInputs.push('gr.Dataframe()'); break; case 'boolean': gradioInputs.push('gr.Checkbox()'); break; case 'any': gradioInputs.push('gr.JSON()'); break; default: gradioInputs.push('gr.Textbox()'); } } } // Map Python types to Gradio components for outputs const gradioOutputs = []; // Only add outputs if they actually exist in the block (R0, R1, etc.) if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) { // Use outputCount_ to ensure we only process actual outputs for (let k = 0; k < block.outputCount_; k++) { const type = block.outputTypes_[k]; switch (type) { case 'integer': gradioOutputs.push('gr.Number()'); break; case 'float': gradioOutputs.push('gr.Number()'); break; case 'string': gradioOutputs.push('gr.Textbox()'); break; case 'list': gradioOutputs.push('gr.Dataframe()'); break; case 'boolean': gradioOutputs.push('gr.Checkbox()'); break; case 'any': gradioOutputs.push('gr.JSON()'); break; default: gradioOutputs.push('gr.Textbox()'); } } } return code; }; forBlock['func_def'] = function (block, generator) { // Ensure the generator is properly initialized for nested blocks like loops if (!generator.nameDB_) { generator.nameDB_ = new Blockly.Names(generator.RESERVED_WORDS_ || []); } // Ensure getDistinctName is available for control flow blocks if (!generator.getDistinctName) { generator.getDistinctName = function (name, type) { return this.nameDB_.getDistinctName(name, type); }; } const name = block.getFieldValue('NAME'); const typedInputs = []; let i = 0; // Build function signature with typed arguments while (block.getInput('X' + i)) { const input = block.getInput('X' + i); if (input && input.connection && input.connection.targetBlock()) { const paramName = (block.inputNames_ && block.inputNames_[i]) || ('arg' + i); const type = (block.inputTypes_ && block.inputTypes_[i]) || 'string'; let pyType; switch (type) { case 'integer': pyType = 'int'; break; case 'float': pyType = 'float'; break; case 'string': pyType = 'str'; break; case 'list': pyType = 'list'; break; case 'boolean': pyType = 'bool'; break; default: pyType = 'Any'; } typedInputs.push(`${paramName}: ${pyType}`); } i++; } let body = generator.statementToCode(block, 'BODY'); let returnStatement = ''; // Check if we have outputs defined and the R inputs exist if (block.outputCount_ && block.outputCount_ > 0 && block.getInput('R0')) { const returnValues = []; for (let r = 0; r < block.outputCount_; r++) { let returnValue = generator.valueToCode(block, 'R' + r, Order.ATOMIC); // Replace placeholder args with actual names if (returnValue && block.inputNames_) { for (let j = 0; j < block.inputNames_.length; j++) { const paramName = block.inputNames_[j]; returnValue = returnValue.replace(new RegExp(`arg${j}\\b`, 'g'), paramName); } } // Type-cast returns to ensure proper Gradio compatibility const outputType = block.outputTypes_[r] || 'string'; if (outputType === 'integer' && returnValue) { returnValue = `int(${returnValue})`; } else if (outputType === 'float' && returnValue) { returnValue = `float(${returnValue})`; } else if (outputType === 'boolean' && returnValue) { returnValue = `bool(${returnValue})`; } returnValues.push(returnValue || 'None'); } if (returnValues.length === 1) { returnStatement = ` return ${returnValues[0]}\n`; } else { returnStatement = ` return (${returnValues.join(', ')})\n`; } } else { // No outputs defined, return None returnStatement = ' return None\n'; } // Construct the function definition let code; if (typedInputs.length > 0) { code = `def ${name}(${typedInputs.join(', ')}):\n${body}${returnStatement}`; } else { code = `def ${name}():\n${body}${returnStatement}`; } // Add output type hints as comments if outputs are defined if (block.outputTypes_ && block.outputTypes_.length > 0) { let outputTypes = []; for (let k = 0; k < block.outputTypes_.length; k++) { const type = block.outputTypes_[k]; const outName = block.outputNames_[k] || ('output' + k); let pyType; switch (type) { case 'integer': pyType = 'int'; break; case 'float': pyType = 'float'; break; case 'string': pyType = 'str'; break; case 'list': pyType = 'list'; break; default: pyType = 'Any'; } outputTypes.push(`${outName}: ${pyType}`); }; } // Return function definition as a string value (not executed immediately) return code; }; forBlock['llm_call'] = function (block, generator) { const model = block.getFieldValue('MODEL'); const prompt = generator.valueToCode(block, 'PROMPT', Order.NONE) || "''"; // Generate code to call an LLM model with a prompt const code = `llm_call(${prompt}, model="${model}")`; return [code, Order.NONE]; }; forBlock['func_call'] = function (block, generator) { // Prefer the serialized function name, fall back to the field if needed const funcName = block.currentFunction_ || block.getFieldValue('FUNC_NAME'); if (!funcName || funcName === 'NONE') { return ['None', Order.ATOMIC]; } // Find the function definition to get parameter info const workspace = block.workspace; const funcBlock = workspace.getAllBlocks(false).find(b => b.type === 'func_def' && b.getFieldValue('NAME') === funcName ); if (!funcBlock) { return ['None', Order.ATOMIC]; } // Build the argument list based on actual inputs on the block const args = []; let i = 0; while (block.getInput('ARG' + i)) { const argValue = generator.valueToCode(block, 'ARG' + i, Order.NONE); args.push(argValue || 'None'); i++; } const code = `${funcName}(${args.join(', ')})`; return [code, Order.FUNCTION_CALL]; }; forBlock['call_api'] = function (block, generator) { const url = generator.valueToCode(block, 'URL', Order.NONE) || "''"; const method = block.getFieldValue('METHOD'); const headers = generator.valueToCode(block, 'HEADERS', Order.NONE) || "''"; // Generate code to call an LLM model with a prompt const code = `call_api(url=${url}, method="${method}", headers=${headers})`; return [code, Order.NONE]; }; forBlock['in_json'] = function (block, generator) { const name = generator.valueToCode(block, 'NAME', Order.NONE); const json = generator.valueToCode(block, 'JSON', Order.NONE); // Generate code to call an LLM model with a prompt const code = `${json}[${name}]`; return [code, Order.NONE]; }; forBlock['make_json'] = function (block, generator) { const pairs = []; let i = 0; // Collect all key-value pairs while (block.getInput('FIELD' + i)) { // Get the key from the text field on the block const keyField = block.getField('KEY' + i); const key = keyField ? keyField.getValue() : (block.fieldKeys_[i] || ('key' + i)); const value = generator.valueToCode(block, 'FIELD' + i, Order.NONE) || "''"; pairs.push(`"${key}": ${value}`); i++; } // Generate valid Python dict syntax const code = pairs.length > 0 ? `{${pairs.join(', ')}}` : '{}'; return [code, Order.ATOMIC]; }; forBlock['lists_contains'] = function (block, generator) { const item = generator.valueToCode(block, 'ITEM', Order.NONE) || "''"; const list = generator.valueToCode(block, 'LIST', Order.NONE) || "[]"; // Generate code to check if item is in list const code = `${item} in ${list}`; return [code, Order.ATOMIC]; }; forBlock['cast_as'] = function (block, generator) { const value = generator.valueToCode(block, 'VALUE', Order.NONE) || "''"; const type = block.getFieldValue('TYPE'); // Generate code to cast value to the specified type const code = `${type}(${value})`; return [code, Order.FUNCTION_CALL]; };