diff --git a/api/.env.example b/api/.env.example index 4a3b1d65af..c0942412ab 100644 --- a/api/.env.example +++ b/api/.env.example @@ -135,4 +135,4 @@ BATCH_UPLOAD_LIMIT=10 # CODE EXECUTION CONFIGURATION CODE_EXECUTION_ENDPOINT= -CODE_EXECUTINO_API_KEY= +CODE_EXECUTION_API_KEY= diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 21a8ca5f9f..adfdf6cc69 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -4,6 +4,7 @@ from typing import Literal, Optional from httpx import post from pydantic import BaseModel from yarl import URL +from core.helper.code_executor.javascript_transformer import NodeJsTemplateTransformer from core.helper.code_executor.jina2_transformer import Jinja2TemplateTransformer from core.helper.code_executor.python_transformer import PythonTemplateTransformer @@ -39,17 +40,20 @@ class CodeExecutor: template_transformer = PythonTemplateTransformer elif language == 'jinja2': template_transformer = Jinja2TemplateTransformer + elif language == 'javascript': + template_transformer = NodeJsTemplateTransformer else: raise CodeExecutionException('Unsupported language') runner = template_transformer.transform_caller(code, inputs) - url = URL(CODE_EXECUTION_ENDPOINT) / 'v1' / 'sandbox' / 'run' headers = { 'X-Api-Key': CODE_EXECUTION_API_KEY } data = { - 'language': language if language != 'jinja2' else 'python3', + 'language': 'python3' if language == 'jinja2' else + 'nodejs' if language == 'javascript' else + 'python3' if language == 'python3' else None, 'code': runner, } diff --git a/api/core/helper/code_executor/javascript_transformer.py b/api/core/helper/code_executor/javascript_transformer.py index f87f5c14cb..cc6ad16c66 100644 --- a/api/core/helper/code_executor/javascript_transformer.py +++ b/api/core/helper/code_executor/javascript_transformer.py @@ -1 +1,53 @@ -# TODO \ No newline at end of file +import json +import re + +from core.helper.code_executor.template_transformer import TemplateTransformer + +NODEJS_RUNNER = """// declare main function here +{{code}} + +// execute main function, and return the result +// inputs is a dict, unstructured inputs +output = main({{inputs}}) + +// convert output to json and print +output = JSON.stringify(output) + +result = `<>${output}<>` + +console.log(result) +""" + + +class NodeJsTemplateTransformer(TemplateTransformer): + @classmethod + def transform_caller(cls, code: str, inputs: dict) -> str: + """ + Transform code to python runner + :param code: code + :param inputs: inputs + :return: + """ + + # transform inputs to json string + inputs_str = json.dumps(inputs, indent=4) + + # replace code and inputs + runner = NODEJS_RUNNER.replace('{{code}}', code) + runner = runner.replace('{{inputs}}', inputs_str) + + return runner + + @classmethod + def transform_response(cls, response: str) -> dict: + """ + Transform response to dict + :param response: response + :return: + """ + # extract result + result = re.search(r'<>(.*)<>', response, re.DOTALL) + if not result: + raise ValueError('Failed to parse result') + result = result.group(1) + return json.loads(result) diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index 2c11e5ba00..5dfe398711 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -15,6 +15,16 @@ MAX_STRING_LENGTH = 1000 MAX_STRING_ARRAY_LENGTH = 30 MAX_NUMBER_ARRAY_LENGTH = 1000 +JAVASCRIPT_DEFAULT_CODE = """function main({args1, args2}) { + return { + result: args1 + args2 + } +}""" + +PYTHON_DEFAULT_CODE = """def main(args1: int, args2: int) -> dict: + return { + "result": args1 + args2, + }""" class CodeNode(BaseNode): _node_data_cls = CodeNodeData @@ -42,9 +52,7 @@ class CodeNode(BaseNode): } ], "code_language": "javascript", - "code": "async function main(arg1, arg2) {\n return new Promise((resolve, reject) => {" - "\n if (true) {\n resolve({\n \"result\": arg1 + arg2" - "\n });\n } else {\n reject(\"e\");\n }\n });\n}", + "code": JAVASCRIPT_DEFAULT_CODE, "outputs": [ { "variable": "result", @@ -68,8 +76,7 @@ class CodeNode(BaseNode): } ], "code_language": "python3", - "code": "def main(\n arg1: int,\n arg2: int,\n) -> int:\n return {\n \"result\": arg1 " - "+ arg2\n }", + "code": PYTHON_DEFAULT_CODE, "outputs": [ { "variable": "result", diff --git a/api/core/workflow/nodes/code/entities.py b/api/core/workflow/nodes/code/entities.py index d4d76c45f9..97e178f5df 100644 --- a/api/core/workflow/nodes/code/entities.py +++ b/api/core/workflow/nodes/code/entities.py @@ -17,4 +17,4 @@ class CodeNodeData(BaseNodeData): variables: list[VariableSelector] code_language: Literal['python3', 'javascript'] code: str - outputs: dict[str, Output] + outputs: dict[str, Output] \ No newline at end of file