diff --git a/api/core/workflow/nodes/http_request/http_executor.py b/api/core/workflow/nodes/http_request/http_executor.py index b448f4c8f9..5acc2e5bde 100644 --- a/api/core/workflow/nodes/http_request/http_executor.py +++ b/api/core/workflow/nodes/http_request/http_executor.py @@ -15,9 +15,9 @@ HTTP_REQUEST_DEFAULT_TIMEOUT = (10, 60) class HttpExecutorResponse: status_code: int headers: dict[str, str] - body: str + body: bytes - def __init__(self, status_code: int, headers: dict[str, str], body: str): + def __init__(self, status_code: int, headers: dict[str, str], body: bytes): """ init """ @@ -25,6 +25,34 @@ class HttpExecutorResponse: self.headers = headers self.body = body + def get_content_type(self) -> str: + """ + get content type + """ + for key, val in self.headers.items(): + if key.lower() == 'content-type': + return val + return '' + + def extract_file(self) -> tuple[str, bytes]: + """ + extract file from response if content type is file related + """ + content_type = self.get_content_type() + file_content_types = ['image', 'audio', 'video'] + for v in file_content_types: + if v in content_type: + return content_type, self.body + + return '', b'' + + @property + def content(self) -> str: + """ + get content + """ + return self.body.decode('utf-8') + class HttpExecutor: server_url: str method: str @@ -192,14 +220,14 @@ class HttpExecutor: for k, v in response.headers.items(): headers[k] = v - return HttpExecutorResponse(response.status_code, headers, response.text) + return HttpExecutorResponse(response.status_code, headers, response.content) elif isinstance(response, requests.Response): # get key-value pairs headers headers = {} for k, v in response.headers.items(): headers[k] = v - return HttpExecutorResponse(response.status_code, headers, response.text) + return HttpExecutorResponse(response.status_code, headers, response.content) else: raise ValueError(f'Invalid response type {type(response)}') diff --git a/api/core/workflow/nodes/http_request/http_file_transformer.py b/api/core/workflow/nodes/http_request/http_file_transformer.py new file mode 100644 index 0000000000..3c500f1bd5 --- /dev/null +++ b/api/core/workflow/nodes/http_request/http_file_transformer.py @@ -0,0 +1,2 @@ +class HttpFileTransformer: + pass \ No newline at end of file diff --git a/api/core/workflow/nodes/http_request/http_request_node.py b/api/core/workflow/nodes/http_request/http_request_node.py index a914ae13ff..e74cdf3145 100644 --- a/api/core/workflow/nodes/http_request/http_request_node.py +++ b/api/core/workflow/nodes/http_request/http_request_node.py @@ -1,10 +1,14 @@ +from mimetypes import guess_extension +from os import path from typing import cast +from core.file.file_obj import FileTransferMethod, FileType, FileVar +from core.tools.tool_file_manager import ToolFileManager 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.http_request.entities import HttpRequestNodeData -from core.workflow.nodes.http_request.http_executor import HttpExecutor +from core.workflow.nodes.http_request.http_executor import HttpExecutor, HttpExecutorResponse from models.workflow import WorkflowNodeExecutionStatus @@ -33,17 +37,20 @@ class HttpRequestNode(BaseNode): inputs=variables, error=str(e), process_data={ - 'request': http_executor.to_raw_request() + 'request': http_executor.to_raw_request(), } ) + + files = self.extract_files(http_executor.server_url, response) return NodeRunResult( status=WorkflowNodeExecutionStatus.SUCCEEDED, inputs=variables, outputs={ 'status_code': response.status_code, - 'body': response.body, - 'headers': response.headers + 'body': response.content if not files else '', + 'headers': response.headers, + 'files': files, }, process_data={ 'request': http_executor.to_raw_request(), @@ -61,3 +68,39 @@ class HttpRequestNode(BaseNode): return { variable_selector.variable: variable_selector.value_selector for variable_selector in node_data.variables } + + def extract_files(self, url: str, response: HttpExecutorResponse) -> list[FileVar]: + """ + Extract files from response + """ + files = [] + mimetype, file_binary = response.extract_file() + # if not image, return directly + if 'image' not in mimetype: + return files + + if mimetype: + # extract filename from url + filename = path.basename(url) + # extract extension if possible + extension = guess_extension(mimetype) or '.bin' + + tool_file = ToolFileManager.create_file_by_raw( + user_id=self.user_id, + tenant_id=self.tenant_id, + conversation_id=None, + file_binary=file_binary, + mimetype=mimetype, + ) + + files.append(FileVar( + tenant_id=self.tenant_id, + type=FileType.IMAGE, + transfer_method=FileTransferMethod.TOOL_FILE, + related_id=tool_file.id, + filename=filename, + extension=extension, + mime_type=mimetype, + )) + + return files \ No newline at end of file