This commit is contained in:
科林_Cyril Pilgrim
2025-02-14 19:40:23 +08:00
parent 2a9fd3a8c7
commit 4e8e3035eb
12 changed files with 141 additions and 7 deletions
+2 -2
View File
@@ -2,11 +2,11 @@
## 1. 项目介绍
ComfyUI-DeepSeek-Toolkit 是一个 ComfyUI 的自定义节点包,旨在帮助用户利用来自中国供应商的强大 AI 大语言模型 (LLM),例如 DeepSeek、Qwen 等。该工具包致力于让更广泛的受众能够访问这些先进的模型,即使是那些计算资源有限的用户。
ComfyUI-DeepSeek-Toolkit 是一个 ComfyUI 的自定义节点包,旨在帮助用户利用来自中国公司的强大 AI 大语言模型 (LLM),例如 DeepSeek、Qwen 等。该工具包致力于让更广泛的受众能够访问这些先进的模型,即使是那些计算资源有限的用户。
该项目的灵感来自于 DeepSeek 和 Qwen 等令人印象深刻的 LLM 的出现。它旨在通过提供轻量级和 API 驱动的解决方案,将这些前沿 AI 技术与日常用户连接起来,从而将这些模型集成到 ComfyUI 工作流程中。
ComfyUI-DeepSeek-Toolkit 的使命是使开发人员和创意人员能够在现成的硬件(如旧笔记本电脑)上完成通常需要大量计算能力的任务。该项目设想了一个 AI 民主化并为所有人所用的未来。
ComfyUI-DeepSeek-Toolkit 的使命是使开发人员和创意人员能够在现成的硬件(如旧笔记本电脑)上完成通常需要大量计算能力的任务。
## 2. 功能特性
+5 -2
View File
@@ -4,6 +4,7 @@ from .py.image_understanding import NODE_CLASS_MAPPINGS as IMAGE_UNDERSTANDING_M
from .py.model_loader import NODE_CLASS_MAPPINGS as MODEL_LOADER_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as MODEL_LOADER_DISPLAY_MAPPINGS
from .py.openai_compatible_loader import NODE_CLASS_MAPPINGS as OPENAI_COMPATIBLE_LOADER_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as OPENAI_COMPATIBLE_LOADER_DISPLAY_MAPPINGS
from .py.image_preprocessor import NODE_CLASS_MAPPINGS as IMAGE_PREPROCESSOR_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as IMAGE_PREPROCESSOR_DISPLAY_MAPPINGS
from .py.VideoFileUploader import NODE_CLASS_MAPPINGS as VIDEO_FILE_UPLOADER_MAPPINGS, NODE_DISPLAY_NAME_MAPPINGS as VIDEO_FILE_UPLOADER_DISPLAY_MAPPINGS # 新增导入
# 合并所有节点映射
NODE_CLASS_MAPPINGS = {
@@ -12,7 +13,8 @@ NODE_CLASS_MAPPINGS = {
**IMAGE_UNDERSTANDING_MAPPINGS,
**MODEL_LOADER_MAPPINGS,
**OPENAI_COMPATIBLE_LOADER_MAPPINGS,
**IMAGE_PREPROCESSOR_MAPPINGS
**IMAGE_PREPROCESSOR_MAPPINGS,
**VIDEO_FILE_UPLOADER_MAPPINGS # 新增合并
}
NODE_DISPLAY_NAME_MAPPINGS = {
@@ -21,7 +23,8 @@ NODE_DISPLAY_NAME_MAPPINGS = {
**IMAGE_UNDERSTANDING_DISPLAY_MAPPINGS,
**MODEL_LOADER_DISPLAY_MAPPINGS,
**OPENAI_COMPATIBLE_LOADER_DISPLAY_MAPPINGS,
**IMAGE_PREPROCESSOR_DISPLAY_MAPPINGS
**IMAGE_PREPROCESSOR_DISPLAY_MAPPINGS,
**VIDEO_FILE_UPLOADER_DISPLAY_MAPPINGS # 新增合并
}
WEB_DIRECTORY = "./web"
Binary file not shown.
Binary file not shown.
+79
View File
@@ -0,0 +1,79 @@
import os
import io
import base64
from typing import Tuple, Optional
import google.generativeai as genai # 导入 Gemini API
import time
import folder_paths
import json
class GoogleDriveUpload:
"""
A ComfyUI node for uploading video files to Gemini via File API.
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"api_key": ("STRING", {"multiline": False, "default": ""}), # API 密钥字符串
"file_path": ("STRING", {"default": "", "label": "Video File Path"}),
"file_name": ("STRING", {"default": "", "label": "File Name"}),
# "mime_type": (["video/mp4", "video/avi", "video/quicktime", "video/x-matroska"], {"default": "video/mp4", "label": "MIME Type"}), # Gemini File API 不需要mime_type
# "folder_id": ("STRING", {"default": "", "label": "Google Drive Folder ID", "description": "Optional: If empty, uploads to root."}), # Gemini File API 不需要folder_id
},
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("file_uri",)
FUNCTION = "upload_file"
CATEGORY = "DeepSeek_Toolkit/Google Drive"
def upload_file(self, api_key: str, file_path: str, file_name: str) -> Tuple[str]:
"""
Uploads a file to Gemini via File API.
Args:
api_key (str): The Gemini API key.
file_path (str): The path to the file to upload.
file_name (str): The desired name for the file on Gemini.
Returns:
str: The URI of the uploaded file.
"""
try:
# 1. 验证 API 密钥
if not api_key:
raise ValueError("API 密钥不能为空")
# 2. 验证文件路径
if not os.path.exists(file_path):
raise FileNotFoundError(f"文件未找到: {file_path}")
# 3. 构建 Gemini API 客户端
genai.configure(api_key=api_key)
model = genai.GenerativeModel(model_name="gemini-1.5-pro") # 假设使用 gemini-1.5-pro 模型
client = model
# 4. 上传文件
print("Uploading file...")
video_file = client.upload(path=file_path, name=file_name)
file_uri = video_file.uri
print(f"Completed upload: {file_uri}")
return (file_uri,)
except Exception as e:
print(f"上传文件时发生错误: {e}")
return (f"Error: {str(e)}",)
# 节点类映射
NODE_CLASS_MAPPINGS = {
"GoogleDriveUpload": GoogleDriveUpload
}
# 节点显示名称映射
NODE_DISPLAY_NAME_MAPPINGS = {
"GoogleDriveUpload": "Google Drive Upload (Gemini File API)"
}
+44
View File
@@ -0,0 +1,44 @@
import folder_paths
import nodes
import os
from typing import Tuple
class VideoFileUploader:
"""
Custom node for uploading video files to Gemini API
"""
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"video_path": ("STRING", {"default": "", "label": "Video File Path"}),
"api_key": ("STRING", {"default": "", "label": "Gemini API Key"}), # 模拟API Key输入
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("video_url",)
FUNCTION = "upload_video"
CATEGORY = "DeepSeek_Toolkit"
def upload_video(self, video_path: str, api_key: str) -> Tuple[str]:
"""
Uploads a video file to the Gemini API using the File API.
"""
# 模拟文件上传
if not os.path.exists(video_path):
raise FileNotFoundError(f"Video file not found: {video_path}")
# 模拟上传过程
print(f"[INFO] 上传视频文件: {video_path}")
# 模拟生成视频URL
video_url = f"gemini://file/{os.path.basename(video_path)}" # 模拟视频URL
print(f"[INFO] 视频上传完成,URL: {video_url}")
return (video_url,)
# 注册节点
NODE_CLASS_MAPPINGS = {"VideoFileUploader": VideoFileUploader}
NODE_DISPLAY_NAME_MAPPINGS = {"VideoFileUploader": "Video File Uploader"}
+2 -1
View File
@@ -3,4 +3,5 @@ from . import image_generation
from . import image_understanding
from . import model_loader
from . import openai_compatible_loader
from . import image_preprocessor
from . import image_preprocessor
from . import VideoFileUploader # 新增导入
Binary file not shown.
Binary file not shown.
Binary file not shown.
+9 -2
View File
@@ -27,6 +27,7 @@ class OpenAICompatibleLoader:
},
"optional": {
"prep_img": ("STRING", {"default": "", "forceInput": True}),
"video_url": ("STRING", {"default": "", "forceInput": True, "label": "Video URL"}), # 新增视频URL输入
"system_prompt": ("STRING", {"default": "你是一个AI大模型", "multiline": True}),
"prompt": ("STRING", {"multiline": True}),
"temperature": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 2.0}),
@@ -70,7 +71,7 @@ class OpenAICompatibleLoader:
def generate(self, base_url: str, api_key: str, prompt: str,
model: str, temperature: float,
max_tokens: int, system_prompt: Optional[str] = None, prep_img: Optional[str] = None, enable_memory: bool = False):
max_tokens: int, system_prompt: Optional[str] = None, prep_img: Optional[str] = None, video_url: Optional[str] = None, enable_memory: bool = False): # 新增video_url参数
content = [] # 将 content 初始化放在函数的最开始
# 移除图像参数类型的日志
@@ -85,6 +86,9 @@ class OpenAICompatibleLoader:
# 将 prep_img 添加到 content 中
content.append({"type": "image_url", "image_url": {"url": prep_img}})
if video_url: # 如果提供了视频URL,则将其添加到content中
content.append({"type": "video_url", "video_url": {"url": video_url}})
if prompt.strip():
content.append({"type": "text", "text": prompt})
@@ -101,7 +105,7 @@ class OpenAICompatibleLoader:
"content": content
})
# 移除带有图像内容的消息日志
elif not prompt.strip() and not system_prompt and prep_img is None: # 更精确的判断用户是否提供了有效输入
elif not prompt.strip() and not system_prompt and prep_img is None and video_url is None: # 更精确的判断用户是否提供了有效输入
raise ValueError("用户输入的 prompt 不能为空")
# 模型选择逻辑 (保持不变)
@@ -216,6 +220,9 @@ class OpenAICompatibleLoader:
else:
image_tokens = 0 # 未知类型,默认为 0
input_tokens += image_tokens
if video_url:
video_tokens = len(video_url) // 1000 # 估算token
input_tokens += video_tokens
try:
time.sleep(1)