This commit is contained in:
yeeyou
2025-10-28 14:46:57 +08:00
parent 5cc26d3a56
commit 1bfcc65e0c
4 changed files with 319 additions and 0 deletions
+133
View File
@@ -0,0 +1,133 @@
# ComfyUI OpenAI Compatible API Node
一个支持 OpenAI 兼容 API 的 ComfyUI 自定义节点,可以调用任何兼容 OpenAI API 格式的大语言模型服务(如 vLLM、LM Studio、Ollama、LocalAI 等)。
## 功能特性
- ✅ 支持纯文本对话
- ✅ 支持图片+文本的视觉对话(Vision API)
- ✅ 完全兼容 OpenAI Chat Completions API 格式
- ✅ 支持自定义 API 端点
- ✅ 支持温度、最大 token 数等参数调节
- ✅ 支持图片细节控制(auto/low/high)
## 安装方法
### 方法一:直接克隆到 custom_nodes 目录
```bash
cd ComfyUI/custom_nodes
git clone https://github.com/your-repo/ComfyUI-OpenAI-Compatible-API.git
```
### 方法二:手动下载
1. 下载本仓库的所有文件
2. 将文件夹复制到 `ComfyUI/custom_nodes/` 目录下
3. 重启 ComfyUI
### 安装依赖
本节点需要以下 Python 包(ComfyUI 通常已包含):
```bash
pip install requests pillow torch numpy
```
## 使用说明
### 基本参数
- **prompt**: 发送给模型的文本提示词
- **endpoint**: API 端点地址(例如:`http://localhost:3010/v1/chat/completions`)
- **model**: 模型名称或路径
- **max_tokens**: 最大生成 token 数(1-32000)
- **temperature**: 温度参数,控制生成的随机性(0.0-2.0)
### 可选参数
- **image**: 图片输入(支持 ComfyUI 的 IMAGE 类型)
- **api_key**: API 密钥(如果服务需要)
- **image_detail**: 图片细节级别
- `auto`: 自动选择
- `low`: 低细节(更快,更便宜)
- `high`: 高细节(更详细,更慢)
## 使用示例
### 示例 1:纯文本对话
1. 添加 `OpenAI Compatible LLM` 节点
2. 设置 `endpoint` 为你的 API 地址
3. 设置 `model` 为模型名称
4. 在 `prompt` 中输入你的问题
5. 连接到输出节点查看结果
### 示例 2:图片+文本对话(视觉模型)
1. 添加 `Load Image` 节点加载图片
2. 添加 `OpenAI Compatible LLM` 节点
3. 将图片输出连接到节点的 `image` 输入
4. 在 `prompt` 中输入关于图片的问题(如 "描述这张图片")
5. 确保 `model` 是支持视觉的模型(如 Qwen-VL、LLaVA 等)
6. 连接到输出节点查看结果
## 兼容的后端服务
本节点兼容任何实现了 OpenAI Chat Completions API 的服务:
- **vLLM**: 高性能推理服务器
- **Ollama**: 本地大模型运行工具
- **LM Studio**: 桌面端大模型应用
- **LocalAI**: 本地 AI API 服务
- **Text Generation WebUI**: OpenAI API 扩展
- **OpenAI 官方 API**: 需要设置正确的 endpoint 和 api_key
## 支持的视觉模型
如果要使用图片输入功能,你的模型需要支持视觉输入,例如:
- Qwen-VL / Qwen2-VL
- LLaVA
- MiniGPT-4
- BLIP-2
- CogVLM
- GPT-4 Vision(通过 OpenAI API)
## 注意事项
1. 确保你的 API 服务正在运行并且可访问
2. 如果使用图片输入,确保模型支持视觉功能
3. 某些服务可能需要 API 密钥
4. 超时时间设置为 120 秒,处理大图片或长文本时可能需要调整
## 故障排除
### 连接错误
- 检查 endpoint 地址是否正确
- 确认服务是否正在运行
- 检查防火墙设置
### 模型不支持图片
- 确认模型是否为视觉模型
- 检查 API 服务的日志
- 尝试纯文本对话测试连接
### API 密钥错误
- 确认 api_key 是否正确
- 检查服务是否需要认证
## 开发和贡献
欢迎提交 Issue 和 Pull Request!
## 许可证
MIT License
## 致谢
感谢 ComfyUI 社区和所有贡献者!
+16
View File
@@ -0,0 +1,16 @@
"""
ComfyUI OpenAI Compatible API Node
支持调用 OpenAI 兼容的 API,包括文本和图片输入
"""
from .openai_llm_node import OpenAICompatibleLLM
NODE_CLASS_MAPPINGS = {
"OpenAICompatibleLLM": OpenAICompatibleLLM
}
NODE_DISPLAY_NAME_MAPPINGS = {
"OpenAICompatibleLLM": "OpenAI Compatible LLM"
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
+166
View File
@@ -0,0 +1,166 @@
"""
OpenAI Compatible API Node for ComfyUI
支持文本和图片输入,调用兼容 OpenAI 的 API
"""
import base64
import io
import json
import requests
from PIL import Image
import torch
import numpy as np
class OpenAICompatibleLLM:
"""
OpenAI 兼容 API 调用节点
支持纯文本对话和带图片的视觉对话
"""
def __init__(self):
pass
@classmethod
def INPUT_TYPES(cls):
return {
"required": {
"prompt": ("STRING", {
"multiline": True,
"default": "hello"
}),
"endpoint": ("STRING", {
"default": "http://localhost:3010/v1/chat/completions"
}),
"model": ("STRING", {
"default": ""
}),
"max_tokens": ("INT", {
"default": 2000,
"min": 1,
"max": 32000,
"step": 1
}),
"temperature": ("FLOAT", {
"default": 0.7,
"min": 0.0,
"max": 2.0,
"step": 0.01
}),
},
"optional": {
"image": ("IMAGE",),
"api_key": ("STRING", {
"default": ""
}),
"image_detail": (["auto", "low", "high"], {
"default": "auto"
}),
}
}
RETURN_TYPES = ("STRING",)
RETURN_NAMES = ("response",)
FUNCTION = "generate"
CATEGORY = "OpenAI Compatible"
def tensor_to_base64(self, tensor_image):
"""
将 ComfyUI 的图片 tensor 转换为 base64 编码的字符串
ComfyUI 图片格式: [batch, height, width, channels] 范围 0-1
"""
# 取第一张图片
if len(tensor_image.shape) == 4:
tensor_image = tensor_image[0]
# 转换为 numpy array 并调整到 0-255 范围
img_np = (tensor_image.cpu().numpy() * 255).astype(np.uint8)
# 转换为 PIL Image
img_pil = Image.fromarray(img_np)
# 转换为 base64
buffered = io.BytesIO()
img_pil.save(buffered, format="PNG")
img_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
return f"data:image/png;base64,{img_base64}"
def generate(self, prompt, endpoint, model, max_tokens, temperature,
image=None, api_key=None, image_detail="auto"):
"""
调用 OpenAI 兼容的 API 生成响应
"""
# 构建消息内容
if image is not None:
# 如果有图片输入,使用视觉模型的格式
image_base64 = self.tensor_to_base64(image)
message_content = [
{
"type": "text",
"text": prompt
},
{
"type": "image_url",
"image_url": {
"url": image_base64,
"detail": image_detail
}
}
]
else:
# 纯文本对话
message_content = prompt
# 构建请求体
payload = {
"model": model,
"messages": [
{
"role": "user",
"content": message_content
}
],
"max_tokens": max_tokens,
"temperature": temperature
}
# 设置请求头
headers = {
"Content-Type": "application/json"
}
if api_key:
headers["Authorization"] = f"Bearer {api_key}"
try:
# 发送请求
response = requests.post(endpoint, headers=headers, json=payload, timeout=120)
response.raise_for_status()
# 解析响应
result = response.json()
# 提取生成的文本
if "choices" in result and len(result["choices"]) > 0:
generated_text = result["choices"][0]["message"]["content"]
return (generated_text,)
else:
return ("Error: No response from API",)
except requests.exceptions.RequestException as e:
error_msg = f"API Request Error: {str(e)}"
if hasattr(e, 'response') and e.response is not None:
try:
error_detail = e.response.json()
error_msg += f"\nDetails: {json.dumps(error_detail, indent=2)}"
except:
error_msg += f"\nResponse: {e.response.text}"
print(error_msg)
return (error_msg,)
except Exception as e:
error_msg = f"Error: {str(e)}"
print(error_msg)
return (error_msg,)
+4
View File
@@ -0,0 +1,4 @@
requests
pillow
torch
numpy