From c2acb2be60509f18890d0f7afce2506248666445 Mon Sep 17 00:00:00 2001 From: Yeuoly <45712896+Yeuoly@users.noreply.github.com> Date: Thu, 18 Apr 2024 08:00:02 +0800 Subject: [PATCH] feat: code (#3557) --- .../helper/code_executor/code_executor.py | 48 +++++++++++------ api/core/tools/provider/_position.yaml | 1 + .../provider/builtin/code/_assets/icon.svg | 1 + api/core/tools/provider/builtin/code/code.py | 8 +++ .../tools/provider/builtin/code/code.yaml | 13 +++++ .../builtin/code/tools/simple_code.py | 22 ++++++++ .../builtin/code/tools/simple_code.yaml | 51 +++++++++++++++++++ api/core/workflow/nodes/code/code_node.py | 2 +- .../template_transform_node.py | 2 +- .../workflow/nodes/__mock/code_executor.py | 2 +- 10 files changed, 132 insertions(+), 18 deletions(-) create mode 100644 api/core/tools/provider/builtin/code/_assets/icon.svg create mode 100644 api/core/tools/provider/builtin/code/code.py create mode 100644 api/core/tools/provider/builtin/code/code.yaml create mode 100644 api/core/tools/provider/builtin/code/tools/simple_code.py create mode 100644 api/core/tools/provider/builtin/code/tools/simple_code.yaml diff --git a/api/core/helper/code_executor/code_executor.py b/api/core/helper/code_executor/code_executor.py index 3221bbe59e..b70f57680d 100644 --- a/api/core/helper/code_executor/code_executor.py +++ b/api/core/helper/code_executor/code_executor.py @@ -30,34 +30,24 @@ class CodeExecutionResponse(BaseModel): class CodeExecutor: @classmethod - def execute_code(cls, language: Literal['python3', 'javascript', 'jinja2'], code: str, inputs: dict) -> dict: + def execute_code(cls, language: Literal['python3', 'javascript', 'jinja2'], preload: str, code: str) -> str: """ Execute code :param language: code language :param code: code - :param inputs: inputs :return: """ - template_transformer = None - if language == 'python3': - template_transformer = PythonTemplateTransformer - elif language == 'jinja2': - template_transformer = Jinja2TemplateTransformer - elif language == 'javascript': - template_transformer = NodeJsTemplateTransformer - else: - raise CodeExecutionException('Unsupported language') - - runner, preload = template_transformer.transform_caller(code, inputs) url = URL(CODE_EXECUTION_ENDPOINT) / 'v1' / 'sandbox' / 'run' + headers = { 'X-Api-Key': CODE_EXECUTION_API_KEY } + data = { 'language': 'python3' if language == 'jinja2' else 'nodejs' if language == 'javascript' else 'python3' if language == 'python3' else None, - 'code': runner, + 'code': code, 'preload': preload } @@ -85,4 +75,32 @@ class CodeExecutor: if response.data.error: raise CodeExecutionException(response.data.error) - return template_transformer.transform_response(response.data.stdout) \ No newline at end of file + return response.data.stdout + + @classmethod + def execute_workflow_code_template(cls, language: Literal['python3', 'javascript', 'jinja2'], code: str, inputs: dict) -> dict: + """ + Execute code + :param language: code language + :param code: code + :param inputs: inputs + :return: + """ + template_transformer = None + if language == 'python3': + template_transformer = PythonTemplateTransformer + elif language == 'jinja2': + template_transformer = Jinja2TemplateTransformer + elif language == 'javascript': + template_transformer = NodeJsTemplateTransformer + else: + raise CodeExecutionException('Unsupported language') + + runner, preload = template_transformer.transform_caller(code, inputs) + + try: + response = cls.execute_code(language, preload, runner) + except CodeExecutionException as e: + raise e + + return template_transformer.transform_response(response) \ No newline at end of file diff --git a/api/core/tools/provider/_position.yaml b/api/core/tools/provider/_position.yaml index 414bd7e38c..778626f1cc 100644 --- a/api/core/tools/provider/_position.yaml +++ b/api/core/tools/provider/_position.yaml @@ -17,6 +17,7 @@ - model.zhipuai - aippt - youtube +- code - wolframalpha - maths - github diff --git a/api/core/tools/provider/builtin/code/_assets/icon.svg b/api/core/tools/provider/builtin/code/_assets/icon.svg new file mode 100644 index 0000000000..b986ed9426 --- /dev/null +++ b/api/core/tools/provider/builtin/code/_assets/icon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/api/core/tools/provider/builtin/code/code.py b/api/core/tools/provider/builtin/code/code.py new file mode 100644 index 0000000000..fae5ecf769 --- /dev/null +++ b/api/core/tools/provider/builtin/code/code.py @@ -0,0 +1,8 @@ +from typing import Any + +from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController + + +class CodeToolProvider(BuiltinToolProviderController): + def _validate_credentials(self, credentials: dict[str, Any]) -> None: + pass \ No newline at end of file diff --git a/api/core/tools/provider/builtin/code/code.yaml b/api/core/tools/provider/builtin/code/code.yaml new file mode 100644 index 0000000000..b0fd0dd587 --- /dev/null +++ b/api/core/tools/provider/builtin/code/code.yaml @@ -0,0 +1,13 @@ +identity: + author: Dify + name: code + label: + en_US: Code Interpreter + zh_Hans: 代码解释器 + pt_BR: Interpretador de Código + description: + en_US: Run a piece of code and get the result back. + zh_Hans: 运行一段代码并返回结果。 + pt_BR: Execute um trecho de código e obtenha o resultado de volta. + icon: icon.svg +credentials_for_provider: diff --git a/api/core/tools/provider/builtin/code/tools/simple_code.py b/api/core/tools/provider/builtin/code/tools/simple_code.py new file mode 100644 index 0000000000..ae9b1cb612 --- /dev/null +++ b/api/core/tools/provider/builtin/code/tools/simple_code.py @@ -0,0 +1,22 @@ +from typing import Any + +from core.helper.code_executor.code_executor import CodeExecutor +from core.tools.entities.tool_entities import ToolInvokeMessage +from core.tools.tool.builtin_tool import BuiltinTool + + +class SimpleCode(BuiltinTool): + def _invoke(self, user_id: str, tool_parameters: dict[str, Any]) -> ToolInvokeMessage | list[ToolInvokeMessage]: + """ + invoke simple code + """ + + language = tool_parameters.get('language', 'python3') + code = tool_parameters.get('code', '') + + if language not in ['python3', 'javascript']: + raise ValueError(f'Only python3 and javascript are supported, not {language}') + + result = CodeExecutor.execute_code(language, '', code) + + return self.create_text_message(result) \ No newline at end of file diff --git a/api/core/tools/provider/builtin/code/tools/simple_code.yaml b/api/core/tools/provider/builtin/code/tools/simple_code.yaml new file mode 100644 index 0000000000..0d7eaf6eee --- /dev/null +++ b/api/core/tools/provider/builtin/code/tools/simple_code.yaml @@ -0,0 +1,51 @@ +identity: + name: simple_code + author: Dify + label: + en_US: Code Interpreter + zh_Hans: 代码解释器 + pt_BR: Interpretador de Código +description: + human: + en_US: Run code and get the result back, when you're using a lower quality model, please make sure there are some tips help LLM to understand how to write the code. + zh_Hans: 运行一段代码并返回结果,当您使用较低质量的模型时,请确保有一些提示帮助LLM理解如何编写代码。 + pt_BR: Execute um trecho de código e obtenha o resultado de volta, quando você estiver usando um modelo de qualidade inferior, certifique-se de que existam algumas dicas para ajudar o LLM a entender como escrever o código. + llm: A tool for running code and getting the result back, but only native packages are allowed, network/IO operations are disabled. and you must use print() or console.log() to output the result or result will be empty. +parameters: + - name: language + type: string + required: true + label: + en_US: Language + zh_Hans: 语言 + pt_BR: Idioma + human_description: + en_US: The programming language of the code + zh_Hans: 代码的编程语言 + pt_BR: A linguagem de programação do código + llm_description: language of the code, only "python3" and "javascript" are supported + form: llm + options: + - value: python3 + label: + en_US: Python3 + zh_Hans: Python3 + pt_BR: Python3 + - value: javascript + label: + en_US: JavaScript + zh_Hans: JavaScript + pt_BR: JavaScript + - name: code + type: string + required: true + label: + en_US: Code + zh_Hans: 代码 + pt_BR: Código + human_description: + en_US: The code to be executed + zh_Hans: 要执行的代码 + pt_BR: O código a ser executado + llm_description: code to be executed, only native packages are allowed, network/IO operations are disabled. + form: llm diff --git a/api/core/workflow/nodes/code/code_node.py b/api/core/workflow/nodes/code/code_node.py index bc1b8d7ce1..e9ff571844 100644 --- a/api/core/workflow/nodes/code/code_node.py +++ b/api/core/workflow/nodes/code/code_node.py @@ -112,7 +112,7 @@ class CodeNode(BaseNode): variables[variable] = value # Run code try: - result = CodeExecutor.execute_code( + result = CodeExecutor.execute_workflow_code_template( language=code_language, code=code, inputs=variables diff --git a/api/core/workflow/nodes/template_transform/template_transform_node.py b/api/core/workflow/nodes/template_transform/template_transform_node.py index 01e3d4702f..9e5cc0c889 100644 --- a/api/core/workflow/nodes/template_transform/template_transform_node.py +++ b/api/core/workflow/nodes/template_transform/template_transform_node.py @@ -52,7 +52,7 @@ class TemplateTransformNode(BaseNode): variables[variable] = value # Run code try: - result = CodeExecutor.execute_code( + result = CodeExecutor.execute_workflow_code_template( language='jinja2', code=node_data.template, inputs=variables diff --git a/api/tests/integration_tests/workflow/nodes/__mock/code_executor.py b/api/tests/integration_tests/workflow/nodes/__mock/code_executor.py index 2eb987181f..f83a41c955 100644 --- a/api/tests/integration_tests/workflow/nodes/__mock/code_executor.py +++ b/api/tests/integration_tests/workflow/nodes/__mock/code_executor.py @@ -26,6 +26,6 @@ def setup_code_executor_mock(request, monkeypatch: MonkeyPatch): yield return - monkeypatch.setattr(CodeExecutor, "execute_code", MockedCodeExecutor.invoke) + monkeypatch.setattr(CodeExecutor, "execute_workflow_code_template", MockedCodeExecutor.invoke) yield monkeypatch.undo()