mirror of
https://github.com/langgenius/dify.git
synced 2026-04-24 09:06:42 +08:00
feat: template transform
This commit is contained in:
parent
3d5f9b5a1e
commit
2895c3bc8c
@ -1,10 +1,11 @@
|
|||||||
from os import environ
|
from os import environ
|
||||||
|
from typing import Literal
|
||||||
|
|
||||||
from httpx import post
|
from httpx import post
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from yarl import URL
|
from yarl import URL
|
||||||
|
|
||||||
from core.workflow.nodes.code.python_template import PythonTemplateTransformer
|
from core.helper.code_executor.python_transformer import PythonTemplateTransformer
|
||||||
|
|
||||||
# Code Executor
|
# Code Executor
|
||||||
CODE_EXECUTION_ENDPOINT = environ.get('CODE_EXECUTION_ENDPOINT', '')
|
CODE_EXECUTION_ENDPOINT = environ.get('CODE_EXECUTION_ENDPOINT', '')
|
||||||
@ -24,7 +25,7 @@ class CodeExecutionResponse(BaseModel):
|
|||||||
|
|
||||||
class CodeExecutor:
|
class CodeExecutor:
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute_code(cls, language: str, code: str, inputs: dict) -> dict:
|
def execute_code(cls, language: Literal['python3', 'javascript', 'jina2'], code: str, inputs: dict) -> dict:
|
||||||
"""
|
"""
|
||||||
Execute code
|
Execute code
|
||||||
:param language: code language
|
:param language: code language
|
||||||
@ -32,7 +33,13 @@ class CodeExecutor:
|
|||||||
:param inputs: inputs
|
:param inputs: inputs
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
runner = PythonTemplateTransformer.transform_caller(code, inputs)
|
template_transformer = None
|
||||||
|
if language == 'python3':
|
||||||
|
template_transformer = PythonTemplateTransformer
|
||||||
|
else:
|
||||||
|
raise CodeExecutionException('Unsupported language')
|
||||||
|
|
||||||
|
runner = template_transformer.transform_caller(code, inputs)
|
||||||
|
|
||||||
url = URL(CODE_EXECUTION_ENDPOINT) / 'v1' / 'sandbox' / 'run'
|
url = URL(CODE_EXECUTION_ENDPOINT) / 'v1' / 'sandbox' / 'run'
|
||||||
headers = {
|
headers = {
|
||||||
@ -67,4 +74,4 @@ class CodeExecutor:
|
|||||||
if response.data.stderr:
|
if response.data.stderr:
|
||||||
raise CodeExecutionException(response.data.stderr)
|
raise CodeExecutionException(response.data.stderr)
|
||||||
|
|
||||||
return PythonTemplateTransformer.transform_response(response.data.stdout)
|
return template_transformer.transform_response(response.data.stdout)
|
||||||
1
api/core/helper/code_executor/javascript_transformer.py
Normal file
1
api/core/helper/code_executor/javascript_transformer.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# TODO
|
||||||
1
api/core/helper/code_executor/jina2_transformer.py
Normal file
1
api/core/helper/code_executor/jina2_transformer.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# TODO
|
||||||
@ -1,6 +1,8 @@
|
|||||||
import json
|
import json
|
||||||
import re
|
import re
|
||||||
|
|
||||||
|
from core.helper.code_executor.template_transformer import TemplateTransformer
|
||||||
|
|
||||||
PYTHON_RUNNER = """# declare main function here
|
PYTHON_RUNNER = """# declare main function here
|
||||||
{{code}}
|
{{code}}
|
||||||
|
|
||||||
@ -19,7 +21,7 @@ print(result)
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
class PythonTemplateTransformer:
|
class PythonTemplateTransformer(TemplateTransformer):
|
||||||
@classmethod
|
@classmethod
|
||||||
def transform_caller(cls, code: str, inputs: dict) -> str:
|
def transform_caller(cls, code: str, inputs: dict) -> str:
|
||||||
"""
|
"""
|
||||||
24
api/core/helper/code_executor/template_transformer.py
Normal file
24
api/core/helper/code_executor/template_transformer.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateTransformer(ABC):
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def transform_caller(cls, code: str, inputs: dict) -> str:
|
||||||
|
"""
|
||||||
|
Transform code to python runner
|
||||||
|
:param code: code
|
||||||
|
:param inputs: inputs
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def transform_response(cls, response: str) -> dict:
|
||||||
|
"""
|
||||||
|
Transform response to dict
|
||||||
|
:param response: response
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
pass
|
||||||
@ -1,9 +1,9 @@
|
|||||||
from typing import Optional, Union, cast
|
from typing import Optional, Union, cast
|
||||||
|
|
||||||
|
from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor
|
||||||
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
||||||
from core.workflow.entities.variable_pool import VariablePool
|
from core.workflow.entities.variable_pool import VariablePool
|
||||||
from core.workflow.nodes.base_node import BaseNode
|
from core.workflow.nodes.base_node import BaseNode
|
||||||
from core.workflow.nodes.code.code_executor import CodeExecutionException, CodeExecutor
|
|
||||||
from core.workflow.nodes.code.entities import CodeNodeData
|
from core.workflow.nodes.code.entities import CodeNodeData
|
||||||
from models.workflow import WorkflowNodeExecutionStatus
|
from models.workflow import WorkflowNodeExecutionStatus
|
||||||
|
|
||||||
|
|||||||
@ -16,6 +16,6 @@ class CodeNodeData(BaseNodeData):
|
|||||||
|
|
||||||
variables: list[VariableSelector]
|
variables: list[VariableSelector]
|
||||||
answer: str
|
answer: str
|
||||||
code_language: str
|
code_language: Literal['python3', 'javascript']
|
||||||
code: str
|
code: str
|
||||||
outputs: dict[str, Output]
|
outputs: dict[str, Output]
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
from typing import cast
|
from typing import cast
|
||||||
|
|
||||||
from core.workflow.entities.base_node_data_entities import BaseNodeData
|
|
||||||
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
||||||
from core.workflow.entities.variable_pool import VariablePool
|
from core.workflow.entities.variable_pool import VariablePool
|
||||||
from core.workflow.nodes.base_node import BaseNode
|
from core.workflow.nodes.base_node import BaseNode
|
||||||
|
|||||||
14
api/core/workflow/nodes/template_transform/entities.py
Normal file
14
api/core/workflow/nodes/template_transform/entities.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from typing import Literal, Union
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
|
from core.workflow.entities.base_node_data_entities import BaseNodeData
|
||||||
|
from core.workflow.entities.variable_entities import VariableSelector
|
||||||
|
|
||||||
|
|
||||||
|
class TemplateTransformNodeData(BaseNodeData):
|
||||||
|
"""
|
||||||
|
Code Node Data.
|
||||||
|
"""
|
||||||
|
variables: list[VariableSelector]
|
||||||
|
template: str
|
||||||
@ -1,9 +1,18 @@
|
|||||||
from typing import Optional
|
from typing import Optional, cast
|
||||||
|
from core.helper.code_executor.code_executor import CodeExecutionException, CodeExecutor
|
||||||
|
from core.workflow.entities.base_node_data_entities import BaseNodeData
|
||||||
|
from core.workflow.entities.node_entities import NodeRunResult, NodeType
|
||||||
|
from core.workflow.entities.variable_pool import VariablePool
|
||||||
|
|
||||||
from core.workflow.nodes.base_node import BaseNode
|
from core.workflow.nodes.base_node import BaseNode
|
||||||
|
from core.workflow.nodes.template_transform.entities import TemplateTransformNodeData
|
||||||
|
from models.workflow import WorkflowNodeExecutionStatus
|
||||||
|
|
||||||
|
|
||||||
class TemplateTransformNode(BaseNode):
|
class TemplateTransformNode(BaseNode):
|
||||||
|
_node_data_cls = TemplateTransformNodeData
|
||||||
|
_node_type = NodeType.TEMPLATE_TRANSFORM
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
|
def get_default_config(cls, filters: Optional[dict] = None) -> dict:
|
||||||
"""
|
"""
|
||||||
@ -23,3 +32,51 @@ class TemplateTransformNode(BaseNode):
|
|||||||
"template": "{{ arg1 }}"
|
"template": "{{ arg1 }}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def _run(self, variable_pool: VariablePool) -> NodeRunResult:
|
||||||
|
"""
|
||||||
|
Run node
|
||||||
|
"""
|
||||||
|
node_data = self.node_data
|
||||||
|
node_data: TemplateTransformNodeData = cast(self._node_data_cls, node_data)
|
||||||
|
|
||||||
|
# Get variables
|
||||||
|
variables = {}
|
||||||
|
for variable_selector in node_data.variables:
|
||||||
|
variable = variable_selector.variable
|
||||||
|
value = variable_pool.get_variable_value(
|
||||||
|
variable_selector=variable_selector.value_selector
|
||||||
|
)
|
||||||
|
|
||||||
|
variables[variable] = value
|
||||||
|
|
||||||
|
# Run code
|
||||||
|
try:
|
||||||
|
result = CodeExecutor.execute_code(
|
||||||
|
language='jina2',
|
||||||
|
code=node_data.template,
|
||||||
|
inputs=variables
|
||||||
|
)
|
||||||
|
except CodeExecutionException as e:
|
||||||
|
return NodeRunResult(
|
||||||
|
inputs=variables,
|
||||||
|
status=WorkflowNodeExecutionStatus.FAILED,
|
||||||
|
error=str(e)
|
||||||
|
)
|
||||||
|
|
||||||
|
return NodeRunResult(
|
||||||
|
status=WorkflowNodeExecutionStatus.SUCCEEDED,
|
||||||
|
inputs=variables,
|
||||||
|
outputs=result['result']
|
||||||
|
)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _extract_variable_selector_to_variable_mapping(cls, node_data: TemplateTransformNodeData) -> dict[list[str], str]:
|
||||||
|
"""
|
||||||
|
Extract variable selector to variable mapping
|
||||||
|
:param node_data: node data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
return {
|
||||||
|
variable_selector.value_selector: variable_selector.variable for variable_selector in node_data.variables
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user