Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComfyUI Tool realtive WS paths #13029

Open
wants to merge 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 17 additions & 4 deletions api/constants/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,27 @@
from configs import dify_config

HIDDEN_VALUE = "[__HIDDEN__]"
UUID_NIL = "00000000-0000-0000-0000-000000000000"
# File types and extensions
AUDIO_EXTENSIONS = ["mp3", "m4a", "wav", "webm", "amr", "ogg"]
AUDIO_EXTENSIONS.extend([ext.upper() for ext in AUDIO_EXTENSIONS])

IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "webp", "gif", "svg"]
IMAGE_EXTENSIONS.extend([ext.upper() for ext in IMAGE_EXTENSIONS])

VIDEO_EXTENSIONS = ["mp4", "mov", "mpeg", "mpga"]
VIDEO_EXTENSIONS.extend([ext.upper() for ext in VIDEO_EXTENSIONS])

AUDIO_EXTENSIONS = ["mp3", "m4a", "wav", "webm", "amr"]
AUDIO_EXTENSIONS.extend([ext.upper() for ext in AUDIO_EXTENSIONS])
MIME_TO_EXTENSION = {
"audio/wav": ".wav",
"audio/x-wav": ".wav",
"audio/wave": ".wav",
"audio/x-pn-wav": ".wav",
}
# File identifiers
DIFY_FILE_IDENTIFIER = "__dify__file__"

# File transfer methods
LOCAL_FILE_TRANSFER = "local_file"
REMOTE_URL_TRANSFER = "remote_url"

if dify_config.ETL_TYPE == "Unstructured":
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "mdx", "pdf", "html", "htm", "xlsx", "xls"]
Expand All @@ -22,3 +32,6 @@
else:
DOCUMENT_EXTENSIONS = ["txt", "markdown", "md", "mdx", "pdf", "html", "htm", "xlsx", "xls", "docx", "csv"]
DOCUMENT_EXTENSIONS.extend([ext.upper() for ext in DOCUMENT_EXTENSIONS])

HIDDEN_VALUE = "[__HIDDEN__]"
UUID_NIL = "00000000-0000-0000-0000-000000000000"
6 changes: 6 additions & 0 deletions api/core/tools/provider/builtin/_positions.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
google: 1
dalle: 2
stable_diffusion: 3
serpapi: 4
wolfram: 5
url_to_base64: 6
3 changes: 2 additions & 1 deletion api/core/tools/provider/builtin/comfyui/comfyui.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def _validate_credentials(self, credentials: dict[str, Any]) -> None:
ws_protocol = "ws"
if base_url.scheme == "https":
ws_protocol = "wss"
ws_address = f"{ws_protocol}://{base_url.authority}/ws?clientId=test123"
base_path = str(base_url.path).rstrip("/")
ws_address = f"{ws_protocol}://{base_url.authority}{base_path}/ws?clientId=test123"

try:
ws.connect(ws_address)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ def open_websocket_connection(self) -> tuple[WebSocket, str]:
ws_protocol = "ws"
if self.base_url.scheme == "https":
ws_protocol = "wss"
ws_address = f"{ws_protocol}://{self.base_url.authority}/ws?clientId={client_id}"
ws_url = self.base_url.with_scheme(ws_protocol).join(URL("ws"))
ws_address = str(ws_url.with_query({"clientId": client_id}))
ws.connect(ws_address)
return ws, client_id

Expand Down
3 changes: 3 additions & 0 deletions api/core/tools/provider/builtin/url_to_base64/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .url_to_base64 import URLToBase64Provider

__all__ = ["URLToBase64Provider"]
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import base64
import logging
from typing import Any

import requests

from core.file.file_manager import download
from core.file.models import File
from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool

logger = logging.getLogger(__name__)


class URLToBase64Converter(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> ToolInvokeMessage:
"""
Конвертирует файл в base64 строку.
Поддерживает как внешние URL, так и локальные файлы Dify.
"""
logger.info(f"Received parameters: {tool_parameters}")

# Получаем источник файла
file_source = tool_parameters.get("file_source")
if not file_source:
return self.create_text_message("Please specify file source (url or local)")

try:
# Обработка внешнего URL
if file_source == "url":
url = tool_parameters.get("url")
if not url:
return self.create_text_message("Please provide a URL")

response = requests.get(url)
response.raise_for_status()
content = response.content

# Обработка локального файла Dify
elif file_source == "local":
file = tool_parameters.get("file")
logger.info(f"File data: {file}")

if not file:
return self.create_text_message("Please provide a file")

if not isinstance(file, File):
logger.error(f"Invalid file type: {type(file)}")
return self.create_text_message("Invalid file type. Expected Dify File object")

# Загружаем содержимое файла
content = download(file)
if not content:
logger.error("Failed to download file content")
return self.create_text_message("Failed to download file content")

logger.info(f"Successfully loaded file: {len(content)} bytes")

else:
return self.create_text_message('Invalid file source. Use "url" or "local"')

# Конвертируем содержимое в base64
base64_content = base64.b64encode(content).decode("utf-8")
logger.info(f"Successfully converted file to base64: {len(base64_content)} characters")
return self.create_text_message(base64_content)

except Exception as e:
logger.exception("Error processing file")
return self.create_text_message(f"Error processing file: {str(e)}")
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
identity:
name: url_to_base64
author: vitalijsatilov
label:
en_US: URL to Base64
zh_Hans: URL转Base64
ru_RU: URL в Base64

description:
human:
en_US: Convert URL or local file to base64 string
zh_Hans: 将URL或本地文件转换为base64字符串
ru_RU: Конвертировать URL или локальный файл в строку base64
llm: Convert a file from URL or local storage to base64 string format

parameters:
- name: file_source
type: string
required: true
label:
en_US: File Source
zh_Hans: 文件来源
ru_RU: Источник файла
human_description:
en_US: Source of the file (url or local)
zh_Hans: 文件来源(url或本地)
ru_RU: Источник файла (url или локальный)
llm_description: Specify the source of the file. Use "url" for external URLs or "local" for files from Dify storage.
options:
- value: url
label:
en_US: URL
zh_Hans: URL
ru_RU: URL
- value: local
label:
en_US: Local File
zh_Hans: 本地文件
ru_RU: Локальный файл
form: llm
- name: url
type: string
required: false
label:
en_US: URL
zh_Hans: URL
ru_RU: URL
human_description:
en_US: URL of the file to convert (required if file_source is "url")
zh_Hans: 要转换的文件的URL(当file_source为"url"时必填)
ru_RU: URL файла для конвертации (обязательно если file_source - "url")
llm_description: URL of the file to convert to base64. Only required when file_source is "url".
form: llm
- name: file
type: file
required: false
label:
en_US: File
zh_Hans: 文件
ru_RU: Файл
human_description:
en_US: Local file to convert (required if file_source is "local")
zh_Hans: 要转换的本地文件(当file_source为"local"时必填)
ru_RU: Локальный файл для конвертации (обязательно если file_source - "local")
llm_description: Local file to convert to base64. Only required when file_source is "local".
form: llm
23 changes: 23 additions & 0 deletions api/core/tools/provider/builtin/url_to_base64/url_to_base64.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
from core.tools.provider.builtin.url_to_base64.tools.url_to_base64_converter import URLToBase64Converter
from core.tools.provider.builtin_tool_provider import BuiltinToolProviderController


class URLToBase64Provider(BuiltinToolProviderController):
@property
def need_credentials(self) -> bool:
"""
Whether the provider needs credentials
"""
return False

def _tools(self) -> list:
return [
URLToBase64Converter(),
]

def _validate_credentials(self, credentials: dict) -> bool:
"""
Validate the credentials.
Our tool doesn't require any credentials, so we always return True.
"""
return True
16 changes: 16 additions & 0 deletions api/core/tools/provider/builtin/url_to_base64/url_to_base64.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
identity:
author: bikevit2008
name: url_to_base64
label:
en_US: URL to Base64
ru_RU: URL в Base64
zh_Hans: URL 转 Base64
description:
en_US: A tool for converting files from URL to base64 string format
ru_RU: Инструмент для конвертации файлов из URL в формат base64
zh_Hans: 将 URL 文件转换为 base64 字符串的工具
icon: icon.svg
tags:
- utilities

credentials_for_provider: {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import base64
from typing import Any, Union

import requests

from core.tools.entities.tool_entities import ToolInvokeMessage
from core.tools.tool.builtin_tool import BuiltinTool


class URLToBase64Tool(BuiltinTool):
def _invoke(
self,
user_id: str,
tool_parameters: dict[str, Any],
) -> Union[ToolInvokeMessage, list[ToolInvokeMessage]]:
"""
Конвертирует файл из URL в base64 строку
"""
url = tool_parameters.get("url", "")
if not url:
return self.create_text_message("Пожалуйста, укажите URL")

try:
# Загружаем файл по URL
response = requests.get(url)
response.raise_for_status() # Проверяем на ошибки

# Конвертируем содержимое в base64
base64_content = base64.b64encode(response.content).decode("utf-8")

# Возвращаем результат
return self.create_text_message(base64_content)

except Exception as e:
return self.create_text_message(f"Ошибка при обработке файла: {str(e)}")

def get_runtime_parameters(self) -> list[dict]:
"""
Определяем параметры инструмента
"""
return [
{
"name": "url",
"type": "string",
"required": True,
"label": "URL файла",
"description": "URL файла, который нужно конвертировать в base64",
}
]
27 changes: 25 additions & 2 deletions api/core/tools/tool_file_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import httpx

from configs import dify_config
from constants import MIME_TO_EXTENSION
from core.helper import ssrf_proxy
from extensions.ext_database import db
from extensions.ext_storage import storage
Expand Down Expand Up @@ -64,7 +65,18 @@ def create_file_by_raw(
file_binary: bytes,
mimetype: str,
) -> ToolFile:
extension = guess_extension(mimetype) or ".bin"
logger.info(f"Creating file with mimetype: {mimetype}")

# Проверяем наше сопоставление MIME-типов
extension = MIME_TO_EXTENSION.get(mimetype)
if not extension:
# Если нет в нашем сопоставлении, пробуем стандартный способ
extension = guess_extension(mimetype)
if not extension:
logger.warning(f"Could not determine extension for mimetype {mimetype}, using .bin")
extension = ".bin"

logger.info(f"Using extension: {extension}")
unique_name = uuid4().hex
filename = f"{unique_name}{extension}"
filepath = f"tools/{tenant_id}/{filename}"
Expand Down Expand Up @@ -102,7 +114,18 @@ def create_file_by_url(
raise ValueError(f"timeout when downloading file from {file_url}")

mimetype = guess_type(file_url)[0] or "octet/stream"
extension = guess_extension(mimetype) or ".bin"
logger.info(f"Creating file from URL with mimetype: {mimetype}")

# Проверяем наше сопоставление MIME-типов
extension = MIME_TO_EXTENSION.get(mimetype)
if not extension:
# Если нет в нашем сопоставлении, пробуем стандартный способ
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write the comments in English only.

extension = guess_extension(mimetype)
if not extension:
logger.warning(f"Could not determine extension for mimetype {mimetype}, using .bin")
extension = ".bin"

logger.info(f"Using extension: {extension}")
unique_name = uuid4().hex
filename = f"{unique_name}{extension}"
filepath = f"tools/{tenant_id}/{filename}"
Expand Down
8 changes: 6 additions & 2 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,9 @@ x-shared-env: &shared-api-worker-env
services:
# API service
api:
image: langgenius/dify-api:0.15.2
build:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please restore the changes here.

dockerfile: ../api/Dockerfile
context: ../api
restart: always
environment:
# Use the shared environment variables.
Expand All @@ -416,7 +418,9 @@ services:
# worker service
# The Celery worker for processing the queue.
worker:
image: langgenius/dify-api:0.15.2
build:
dockerfile: ../api/Dockerfile
context: ../api
restart: always
environment:
# Use the shared environment variables.
Expand Down
Loading