Browse Source

feat: code (#3557)

Yeuoly 1 year ago
parent
commit
c2acb2be60

+ 33 - 15
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)
+        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)

+ 1 - 0
api/core/tools/provider/_position.yaml

@@ -17,6 +17,7 @@
 - model.zhipuai
 - aippt
 - youtube
+- code
 - wolframalpha
 - maths
 - github

+ 1 - 0
api/core/tools/provider/builtin/code/_assets/icon.svg

@@ -0,0 +1 @@
+<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg" class="w-3.5 h-3.5" data-icon="Code" aria-hidden="true"><g id="icons/code"><path id="Vector (Stroke)" fill-rule="evenodd" clip-rule="evenodd" d="M8.32593 1.69675C8.67754 1.78466 8.89132 2.14096 8.80342 2.49257L6.47009 11.8259C6.38218 12.1775 6.02588 12.3913 5.67427 12.3034C5.32265 12.2155 5.10887 11.8592 5.19678 11.5076L7.53011 2.17424C7.61801 1.82263 7.97431 1.60885 8.32593 1.69675ZM3.96414 4.20273C4.22042 4.45901 4.22042 4.87453 3.96413 5.13081L2.45578 6.63914C2.45577 6.63915 2.45578 6.63914 2.45578 6.63914C2.25645 6.83851 2.25643 7.16168 2.45575 7.36103C2.45574 7.36103 2.45576 7.36104 2.45575 7.36103L3.96413 8.86936C4.22041 9.12564 4.22042 9.54115 3.96414 9.79744C3.70787 10.0537 3.29235 10.0537 3.03607 9.79745L1.52769 8.28913C0.815811 7.57721 0.815803 6.42302 1.52766 5.7111L3.03606 4.20272C3.29234 3.94644 3.70786 3.94644 3.96414 4.20273ZM10.0361 4.20273C10.2923 3.94644 10.7078 3.94644 10.9641 4.20272L12.4725 5.71108C13.1843 6.423 13.1844 7.57717 12.4725 8.28909L10.9641 9.79745C10.7078 10.0537 10.2923 10.0537 10.036 9.79744C9.77977 9.54115 9.77978 9.12564 10.0361 8.86936L11.5444 7.36107C11.7437 7.16172 11.7438 6.83854 11.5444 6.63917C11.5444 6.63915 11.5445 6.63918 11.5444 6.63917L10.0361 5.13081C9.77978 4.87453 9.77978 4.45901 10.0361 4.20273Z" fill="currentColor"></path></g></svg>

+ 8 - 0
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

+ 13 - 0
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:

+ 22 - 0
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)

+ 51 - 0
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

+ 1 - 1
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

+ 1 - 1
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

+ 1 - 1
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()