240 lines
7.6 KiB
Python
240 lines
7.6 KiB
Python
import requests
|
|
import json
|
|
import time
|
|
import torch
|
|
import numpy as np
|
|
from PIL import Image
|
|
from io import BytesIO
|
|
import os
|
|
import base64
|
|
import tempfile
|
|
|
|
try:
|
|
from openai import OpenAI
|
|
OPENAI_AVAILABLE = True
|
|
except ImportError:
|
|
print("⚠️ 警告: 未安装openai库,图生文功能将不可用")
|
|
print("请运行: pip install openai")
|
|
OPENAI_AVAILABLE = False
|
|
OpenAI = None
|
|
|
|
def load_config():
|
|
config_path = os.path.join(os.path.dirname(__file__), 'modelscope_config.json')
|
|
try:
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
|
return json.load(f)
|
|
except:
|
|
return {
|
|
"default_model": "Qwen/Qwen-Image",
|
|
"timeout": 720,
|
|
"image_download_timeout": 30,
|
|
"default_prompt": "A beautiful landscape",
|
|
"api_token": "" # 确保默认默认配置中添加api_token字段
|
|
}
|
|
|
|
def save_config(config):
|
|
"""保存配置到modelscope_config.json"""
|
|
config_path = os.path.join(os.path.dirname(__file__), 'modelscope_config.json')
|
|
try:
|
|
with open(config_path, 'w', encoding='utf-8') as f:
|
|
json.dump(config, f, ensure_ascii=False, indent=2)
|
|
return True
|
|
except Exception as e:
|
|
print(f"保存配置失败: {e}")
|
|
return False
|
|
|
|
def load_api_token():
|
|
"""仅从modelscope_config.json读取API Token"""
|
|
try:
|
|
cfg = load_config()
|
|
return cfg.get("api_token", "").strip()
|
|
except Exception as e:
|
|
print(f"读取config.json中的token失败: {e}")
|
|
return ""
|
|
|
|
def save_api_token(token):
|
|
"""仅将API Token保存到modelscope_config.json"""
|
|
try:
|
|
cfg = load_config()
|
|
cfg["api_token"] = token.strip()
|
|
return save_config(cfg)
|
|
except Exception as e:
|
|
print(f"保存token失败: {e}")
|
|
return False
|
|
|
|
def tensor_to_base64_url(image_tensor):
|
|
try:
|
|
if len(image_tensor.shape) == 4:
|
|
image_tensor = image_tensor.squeeze(0)
|
|
|
|
if image_tensor.max() <= 1.0:
|
|
image_np = (image_tensor.cpu().numpy() * 255).astype(np.uint8)
|
|
else:
|
|
image_np = image_tensor.cpu().numpy().astype(np.uint8)
|
|
|
|
pil_image = Image.fromarray(image_np)
|
|
|
|
buffer = BytesIO()
|
|
pil_image.save(buffer, format='JPEG', quality=85)
|
|
img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
|
|
|
return f"data:image/jpeg;base64,{img_base64}"
|
|
|
|
except Exception as e:
|
|
print(f"图像转换失败: {e}")
|
|
raise Exception(f"图像格式转换失败: {str(e)}")
|
|
|
|
class ModelScopeVisionNode:
|
|
def __init__(self):
|
|
pass
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
if not OPENAI_AVAILABLE:
|
|
return {
|
|
"required": {
|
|
"error_message": ("STRING", {
|
|
"default": "请先安装openai库: pip install openai",
|
|
"multiline": True
|
|
}),
|
|
}
|
|
}
|
|
config = load_config()
|
|
saved_token = load_api_token()
|
|
return {
|
|
"required": {
|
|
"image": ("IMAGE",),
|
|
"prompt": ("STRING", {
|
|
"multiline": True,
|
|
"default": config.get("default_prompt", "描述这幅图")
|
|
}),
|
|
"api_token": ("STRING", {
|
|
"default": "",
|
|
"placeholder": "请输入您的魔搭API Token",
|
|
"multiline": False
|
|
}),
|
|
},
|
|
"optional": {
|
|
"model": (config.get("vision_models", ["stepfun-ai/step3"]), {
|
|
"default": config.get("default_vision_model", "stepfun-ai/step3")
|
|
}),
|
|
"max_tokens": ("INT", {
|
|
"default": 1000,
|
|
"min": 100,
|
|
"max": 4000
|
|
}),
|
|
"temperature": ("FLOAT", {
|
|
"default": 0.7,
|
|
"min": 0.1,
|
|
"max": 2.0,
|
|
"step": 0.1
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("description",)
|
|
FUNCTION = "analyze_image"
|
|
CATEGORY = "ModelScopeAPI"
|
|
|
|
def analyze_image(self, image=None, prompt="", api_token="", model="stepfun-ai/step3", max_tokens=1000, temperature=0.7, error_message=""):
|
|
if not OPENAI_AVAILABLE:
|
|
return ("请先安装openai库: pip install openai",)
|
|
|
|
config = load_config()
|
|
|
|
if not api_token or api_token.strip() == "":
|
|
api_token = load_api_token()
|
|
if not api_token or api_token.strip() == "":
|
|
raise Exception("请输入有效的API Token或确保已保存token")
|
|
|
|
saved_token = load_api_token()
|
|
if api_token != saved_token:
|
|
if save_api_token(api_token):
|
|
print("✅ API Token已自动保存到modelscope_config.json")
|
|
else:
|
|
print("⚠️ API Token保存失败,但不影响当前使用")
|
|
|
|
try:
|
|
print(f"🔍 开始分析图像...")
|
|
print(f"📝 提示词: {prompt}")
|
|
print(f"🤖 模型: {model}")
|
|
|
|
image_url = tensor_to_base64_url(image)
|
|
print(f"🖼️ 图像已转换为base64格式")
|
|
|
|
client = OpenAI(
|
|
base_url='https://api-inference.modelscope.cn/v1',
|
|
api_key=api_token
|
|
)
|
|
|
|
messages = [{
|
|
'role': 'user',
|
|
'content': [{
|
|
'type': 'text',
|
|
'text': prompt,
|
|
}, {
|
|
'type': 'image_url',
|
|
'image_url': {
|
|
'url': image_url,
|
|
},
|
|
}],
|
|
}]
|
|
|
|
print(f"🚀 发送API请求...")
|
|
|
|
response = client.chat.completions.create(
|
|
model=model,
|
|
messages=messages,
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
stream=False
|
|
)
|
|
|
|
description = response.choices[0].message.content
|
|
print(f"✅ 分析完成!")
|
|
print(f"📄 结果: {description[:100]}...")
|
|
|
|
return (description,)
|
|
|
|
except Exception as e:
|
|
error_msg = f"图像分析失败: {str(e)}"
|
|
print(f"❌ {error_msg}")
|
|
return (error_msg,)
|
|
|
|
if OPENAI_AVAILABLE:
|
|
NODE_CLASS_MAPPINGS = {
|
|
"ModelScopeVisionNode": ModelScopeVisionNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"ModelScopeVisionNode": "ModelScope-Vision 图生文节点"
|
|
}
|
|
else:
|
|
class OpenAINotInstalledNode:
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"install_command": ("STRING", {
|
|
"default": "pip install openai",
|
|
"multiline": False
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("message",)
|
|
FUNCTION = "show_install_message"
|
|
CATEGORY = "ModelScopeAPI"
|
|
|
|
def show_install_message(self, install_command):
|
|
return ("请先安装openai库才能使用图生文功能: " + install_command,)
|
|
|
|
NODE_CLASS_MAPPINGS = {
|
|
"ModelScopeVisionNode": OpenAINotInstalledNode
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"ModelScopeVisionNode": "ModelScope-Vision 图生文节点 (需要安装openai)"
|
|
} |